aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mpack/lmpack.c3
-rw-r--r--src/nvim/CMakeLists.txt13
-rw-r--r--src/nvim/api/buffer.c7
-rw-r--r--src/nvim/api/private/helpers.c2
-rw-r--r--src/nvim/api/vim.c14
-rw-r--r--src/nvim/aucmd.c1
-rw-r--r--src/nvim/auevents.lua5
-rw-r--r--src/nvim/autocmd.c8
-rw-r--r--src/nvim/buffer.c3
-rw-r--r--src/nvim/buffer_defs.h6
-rw-r--r--src/nvim/change.c291
-rw-r--r--src/nvim/change.h7
-rw-r--r--src/nvim/channel.c1
-rw-r--r--src/nvim/charset.c1
-rw-r--r--src/nvim/cursor.c1
-rw-r--r--src/nvim/diff.c2
-rw-r--r--src/nvim/digraph.c1
-rw-r--r--src/nvim/edit.c20
-rw-r--r--src/nvim/eval.c26
-rw-r--r--src/nvim/eval.h7
-rw-r--r--src/nvim/eval.lua2
-rw-r--r--src/nvim/eval/funcs.c103
-rw-r--r--src/nvim/eval/typval.c3
-rw-r--r--src/nvim/eval/userfunc.c1
-rw-r--r--src/nvim/event/rstream.c2
-rw-r--r--src/nvim/ex_cmds.c5
-rw-r--r--src/nvim/ex_cmds2.c2
-rw-r--r--src/nvim/ex_docmd.c46
-rw-r--r--src/nvim/ex_docmd.h1
-rw-r--r--src/nvim/ex_getln.c1
-rw-r--r--src/nvim/ex_session.c1
-rw-r--r--src/nvim/file_search.c16
-rw-r--r--src/nvim/fileio.c2
-rw-r--r--src/nvim/fold.c2
-rw-r--r--src/nvim/generators/gen_char_blob.lua52
-rw-r--r--src/nvim/getchar.c15
-rw-r--r--src/nvim/globals.h4
-rw-r--r--src/nvim/highlight_defs.h4
-rw-r--r--src/nvim/indent.c1
-rw-r--r--src/nvim/indent_c.c1
-rw-r--r--src/nvim/input.c255
-rw-r--r--src/nvim/input.h9
-rw-r--r--src/nvim/lua/executor.c21
-rw-r--r--src/nvim/lua/spell.c99
-rw-r--r--src/nvim/lua/spell.h12
-rw-r--r--src/nvim/lua/stdlib.c12
-rw-r--r--src/nvim/main.c45
-rw-r--r--src/nvim/mbyte.c12
-rw-r--r--src/nvim/memline.c13
-rw-r--r--src/nvim/memory.c1
-rw-r--r--src/nvim/menu.c1
-rw-r--r--src/nvim/message.c49
-rw-r--r--src/nvim/misc1.c1116
-rw-r--r--src/nvim/misc1.h17
-rw-r--r--src/nvim/mouse.c27
-rw-r--r--src/nvim/move.c6
-rw-r--r--src/nvim/msgpack_rpc/channel.c1
-rw-r--r--src/nvim/normal.c22
-rw-r--r--src/nvim/ops.c22
-rw-r--r--src/nvim/option.c39
-rw-r--r--src/nvim/option.h1
-rw-r--r--src/nvim/option_defs.h1
-rw-r--r--src/nvim/options.lua8
-rw-r--r--src/nvim/os/fs.c1
-rw-r--r--src/nvim/os/input.c35
-rw-r--r--src/nvim/os/os_defs.h4
-rw-r--r--src/nvim/os/pty_process_unix.c122
-rw-r--r--src/nvim/os/shell.c113
-rw-r--r--src/nvim/os/signal.c1
-rw-r--r--src/nvim/os/users.c60
-rw-r--r--src/nvim/os_unix.c1
-rw-r--r--src/nvim/path.c12
-rw-r--r--src/nvim/popupmnu.c43
-rw-r--r--src/nvim/quickfix.c4
-rw-r--r--src/nvim/regexp.c2
-rw-r--r--src/nvim/regexp_nfa.c39
-rw-r--r--src/nvim/runtime.c1
-rw-r--r--src/nvim/screen.c64
-rw-r--r--src/nvim/search.c7
-rw-r--r--src/nvim/sign.c64
-rw-r--r--src/nvim/sign_defs.h1
-rw-r--r--src/nvim/spell.c2
-rw-r--r--src/nvim/spellfile.c13
-rw-r--r--src/nvim/state.c32
-rw-r--r--src/nvim/strings.c13
-rw-r--r--src/nvim/syntax.c15
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/terminal.c21
-rw-r--r--src/nvim/testdir/runtest.vim6
-rw-r--r--src/nvim/testdir/setup.vim2
-rw-r--r--src/nvim/testdir/test_autocmd.vim90
-rw-r--r--src/nvim/testdir/test_blob.vim8
-rw-r--r--src/nvim/testdir/test_cmdline.vim37
-rw-r--r--src/nvim/testdir/test_display.vim25
-rw-r--r--src/nvim/testdir/test_edit.vim3
-rw-r--r--src/nvim/testdir/test_ex_mode.vim2
-rw-r--r--src/nvim/testdir/test_filechanged.vim149
-rw-r--r--src/nvim/testdir/test_fileformat.vim246
-rw-r--r--src/nvim/testdir/test_filetype.vim50
-rw-r--r--src/nvim/testdir/test_highlight.vim7
-rw-r--r--src/nvim/testdir/test_listchars.vim16
-rw-r--r--src/nvim/testdir/test_messages.vim8
-rw-r--r--src/nvim/testdir/test_options.vim21
-rw-r--r--src/nvim/testdir/test_signs.vim57
-rw-r--r--src/nvim/testdir/test_substitute.vim2
-rw-r--r--src/nvim/testdir/test_timers.vim2
-rw-r--r--src/nvim/testdir/test_visual.vim2
-rw-r--r--src/nvim/tui/input.c6
-rw-r--r--src/nvim/tui/tui.c43
-rw-r--r--src/nvim/ui.c39
-rw-r--r--src/nvim/undo.c37
-rw-r--r--src/nvim/vim.h5
-rw-r--r--src/nvim/window.c1
113 files changed, 2448 insertions, 1560 deletions
diff --git a/src/mpack/lmpack.c b/src/mpack/lmpack.c
index 87acf46592..126f2f3824 100644
--- a/src/mpack/lmpack.c
+++ b/src/mpack/lmpack.c
@@ -595,6 +595,7 @@ static void lmpack_unparse_enter(mpack_parser_t *parser, mpack_node_t *node)
/* push the pair */
result = lua_next(L, -2);
assert(result); /* should not be here if the map was fully processed */
+ (void)result; /* ignore unused warning */
if (parent->key_visited) {
/* release the current key */
lmpack_unref(L, packer->reg, (int)parent->data[1].i);
@@ -1010,6 +1011,7 @@ static int lmpack_session_reply(lua_State *L)
"invalid request id");
result = mpack_rpc_reply(session->session, &b, &bl, (mpack_uint32_t)id);
assert(result == MPACK_OK);
+ (void)result; /* ignore unused warning */
lua_pushlstring(L, buf, sizeof(buf) - bl);
return 1;
}
@@ -1027,6 +1029,7 @@ static int lmpack_session_notify(lua_State *L)
session = lmpack_check_session(L, 1);
result = mpack_rpc_notify(session->session, &b, &bl);
assert(result == MPACK_OK);
+ (void)result; /* ignore unused warning */
lua_pushlstring(L, buf, sizeof(buf) - bl);
return 1;
}
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 185d55daed..9c4b778169 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -326,7 +326,9 @@ add_custom_command(
add_custom_command(
OUTPUT ${VIM_MODULE_FILE}
- COMMAND ${LUA_PRG} ${CHAR_BLOB_GENERATOR} ${VIM_MODULE_FILE}
+ COMMAND ${CMAKE_COMMAND} -E env
+ "LUAC_PRG=${LUAC_PRG}"
+ ${LUA_PRG} ${CHAR_BLOB_GENERATOR} -c ${VIM_MODULE_FILE}
${LUA_VIM_MODULE_SOURCE} vim_module
${LUA_SHARED_MODULE_SOURCE} shared_module
${LUA_INSPECT_MODULE_SOURCE} inspect_module
@@ -339,6 +341,7 @@ add_custom_command(
${LUA_INSPECT_MODULE_SOURCE}
${LUA_F_MODULE_SOURCE}
${LUA_META_MODULE_SOURCE}
+ VERBATIM
)
list(APPEND NVIM_GENERATED_SOURCES
@@ -468,9 +471,11 @@ list(APPEND NVIM_LINK_LIBRARIES
if(UNIX)
list(APPEND NVIM_LINK_LIBRARIES
- m
- util
- )
+ m)
+ if (NOT CMAKE_SYSTEM_NAME STREQUAL "SunOS")
+ list(APPEND NVIM_LINK_LIBRARIES
+ util)
+ endif()
endif()
set(NVIM_EXEC_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES} ${LUA_PREFERRED_LIBRARIES})
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 718743ed9c..ce9c4e27ad 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -12,24 +12,19 @@
#include "nvim/api/buffer.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
-#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/extmark.h"
-#include "nvim/fileio.h"
-#include "nvim/getchar.h"
#include "nvim/lua/executor.h"
-#include "nvim/map.h"
-#include "nvim/map_defs.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/ops.h"
#include "nvim/undo.h"
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index d470def277..9b407eab8b 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -513,7 +513,7 @@ String cbuf_to_string(const char *buf, size_t size)
String cstrn_to_string(const char *str, size_t maxsize)
FUNC_ATTR_NONNULL_ALL
{
- return cbuf_to_string(str, strnlen(str, maxsize));
+ return cbuf_to_string(str, STRNLEN(str, maxsize));
}
/// Creates a String using the given C string. Unlike
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 4f7c320129..f81cdf9deb 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -696,7 +696,17 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
rv = INTEGER_OBJ(numval);
break;
case 2:
- rv = BOOLEAN_OBJ(!!numval);
+ switch (numval) {
+ case 0:
+ case 1:
+ rv = BOOLEAN_OBJ(numval);
+ break;
+ default:
+ // Boolean options that return something other than 0 or 1 should return nil. Currently this
+ // only applies to 'autoread' which uses -1 as a local value to indicate "unset"
+ rv = NIL;
+ break;
+ }
break;
default:
api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data);
@@ -749,7 +759,7 @@ void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error
stringval = value.data.string.data;
break;
case kObjectTypeNil:
- // Do nothing
+ scope |= OPT_CLEAR;
break;
default:
api_set_error(err, kErrorTypeValidation, "invalid value for option");
diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c
index a236b47027..d7f73fa4a1 100644
--- a/src/nvim/aucmd.c
+++ b/src/nvim/aucmd.c
@@ -8,7 +8,6 @@
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/main.h"
-#include "nvim/misc1.h"
#include "nvim/os/os.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 1daae85c5e..8fe623fc96 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -75,7 +75,10 @@ return {
'QuickFixCmdPost', -- after :make, :grep etc.
'QuickFixCmdPre', -- before :make, :grep etc.
'QuitPre', -- before :quit
+ 'RecordingEnter', -- when starting to record a macro
+ 'RecordingLeave', -- just before a macro stops recording
'RemoteReply', -- upon string reception from a remote vim
+ 'SearchWrapped', -- after the search wrapped around
'SessionLoadPost', -- after loading a session file
'ShellCmdPost', -- after ":!cmd"
'ShellFilterPost', -- after ":1,2!cmd", ":w !cmd", ":r !cmd".
@@ -130,6 +133,8 @@ return {
BufModifiedSet=true,
DiagnosticChanged=true,
DirChanged=true,
+ RecordingEnter=true,
+ RecordingLeave=true,
Signal=true,
TabClosed=true,
TabNew=true,
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 9044657358..463bd5e0e6 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -15,8 +15,8 @@
#include "nvim/ex_docmd.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
-#include "nvim/misc1.h"
#include "nvim/option.h"
+#include "nvim/os/input.h"
#include "nvim/regexp.h"
#include "nvim/search.h"
#include "nvim/state.h"
@@ -932,6 +932,12 @@ static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, c
last_mode = get_mode();
}
+ // If the event is CursorMoved, update the last cursor position
+ // position to avoid immediately triggering the autocommand
+ if (event == EVENT_CURSORMOVED && !has_event(EVENT_CURSORMOVED)) {
+ curwin->w_last_cursormoved = curwin->w_cursor;
+ }
+
ap->cmds = NULL;
*prev_ap = ap;
last_autopat[(int)event] = ap;
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index f2f4950e58..89baea83f8 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -57,7 +57,6 @@
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
@@ -2719,7 +2718,7 @@ void buflist_list(exarg_T *eap)
IObuff[len++] = ' ';
} while (--i > 0 && len < IOSIZE - 18);
if (vim_strchr(eap->arg, 't') && buf->b_last_used) {
- add_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used);
+ undo_fmt_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used);
} else {
vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len),
_("line %" PRId64),
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index e53b2d1dfa..49e527e98b 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -1116,12 +1116,6 @@ typedef struct {
pos_T w_cursor_corr; // corrected cursor position
} pos_save_T;
-// Struct passed to get_v_event() and restore_v_event().
-typedef struct {
- bool sve_did_save;
- hashtab_T sve_hashtab;
-} save_v_event_T;
-
/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode
/// \addtogroup MENU_INDEX
/// @{
diff --git a/src/nvim/change.c b/src/nvim/change.c
index ef771125f1..1dbbfff024 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -19,7 +19,6 @@
#include "nvim/indent_c.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
@@ -131,7 +130,7 @@ void changed_internal(void)
curbuf->b_changed = true;
curbuf->b_changed_invalid = true;
ml_setflags(curbuf);
- check_status(curbuf);
+ redraw_buf_status_later(curbuf);
redraw_tabline = true;
need_maketitle = true; // set window title later
}
@@ -517,7 +516,7 @@ void unchanged(buf_T *buf, int ff, bool always_inc_changedtick)
if (ff) {
save_file_ff(buf);
}
- check_status(buf);
+ redraw_buf_status_later(buf);
redraw_tabline = true;
need_maketitle = true; // set window title later
buf_inc_changedtick(buf);
@@ -1863,3 +1862,289 @@ void del_lines(long nlines, bool undo)
// adjust marks, mark the buffer as changed and prepare for displaying
deleted_lines_mark(first, n);
}
+
+/// Returns the length in bytes of the prefix of the given string which introduces a comment.
+///
+/// If this string is not a comment then 0 is returned.
+/// When "flags" is not NULL, it is set to point to the flags of the recognized comment leader.
+/// "backward" must be true for the "O" command.
+/// If "include_space" is set, include trailing whitespace while calculating the length.
+int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_space)
+{
+ int i, j;
+ int result;
+ int got_com = false;
+ int found_one;
+ char_u part_buf[COM_MAX_LEN]; // buffer for one option part
+ char_u *string; // pointer to comment string
+ char_u *list;
+ int middle_match_len = 0;
+ char_u *prev_list;
+ char_u *saved_flags = NULL;
+
+ result = i = 0;
+ while (ascii_iswhite(line[i])) { // leading white space is ignored
+ i++;
+ }
+
+ // Repeat to match several nested comment strings.
+ while (line[i] != NUL) {
+ // scan through the 'comments' option for a match
+ found_one = false;
+ for (list = curbuf->b_p_com; *list;) {
+ // Get one option part into part_buf[]. Advance "list" to next
+ // one. Put "string" at start of string.
+ if (!got_com && flags != NULL) {
+ *flags = list; // remember where flags started
+ }
+ prev_list = list;
+ (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
+ string = vim_strchr(part_buf, ':');
+ if (string == NULL) { // missing ':', ignore this part
+ continue;
+ }
+ *string++ = NUL; // isolate flags from string
+
+ // If we found a middle match previously, use that match when this
+ // is not a middle or end.
+ if (middle_match_len != 0
+ && vim_strchr(part_buf, COM_MIDDLE) == NULL
+ && vim_strchr(part_buf, COM_END) == NULL) {
+ break;
+ }
+
+ // When we already found a nested comment, only accept further
+ // nested comments.
+ if (got_com && vim_strchr(part_buf, COM_NEST) == NULL) {
+ continue;
+ }
+
+ // When 'O' flag present and using "O" command skip this one.
+ if (backward && vim_strchr(part_buf, COM_NOBACK) != NULL) {
+ continue;
+ }
+
+ // Line contents and string must match.
+ // When string starts with white space, must have some white space
+ // (but the amount does not need to match, there might be a mix of
+ // TABs and spaces).
+ if (ascii_iswhite(string[0])) {
+ if (i == 0 || !ascii_iswhite(line[i - 1])) {
+ continue; // missing white space
+ }
+ while (ascii_iswhite(string[0])) {
+ string++;
+ }
+ }
+ for (j = 0; string[j] != NUL && string[j] == line[i + j]; j++) {
+ }
+ if (string[j] != NUL) {
+ continue; // string doesn't match
+ }
+ // When 'b' flag used, there must be white space or an
+ // end-of-line after the string in the line.
+ if (vim_strchr(part_buf, COM_BLANK) != NULL
+ && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) {
+ continue;
+ }
+
+ // We have found a match, stop searching unless this is a middle
+ // comment. The middle comment can be a substring of the end
+ // comment in which case it's better to return the length of the
+ // end comment and its flags. Thus we keep searching with middle
+ // and end matches and use an end match if it matches better.
+ if (vim_strchr(part_buf, COM_MIDDLE) != NULL) {
+ if (middle_match_len == 0) {
+ middle_match_len = j;
+ saved_flags = prev_list;
+ }
+ continue;
+ }
+ if (middle_match_len != 0 && j > middle_match_len) {
+ // Use this match instead of the middle match, since it's a
+ // longer thus better match.
+ middle_match_len = 0;
+ }
+
+ if (middle_match_len == 0) {
+ i += j;
+ }
+ found_one = true;
+ break;
+ }
+
+ if (middle_match_len != 0) {
+ // Use the previously found middle match after failing to find a
+ // match with an end.
+ if (!got_com && flags != NULL) {
+ *flags = saved_flags;
+ }
+ i += middle_match_len;
+ found_one = true;
+ }
+
+ // No match found, stop scanning.
+ if (!found_one) {
+ break;
+ }
+
+ result = i;
+
+ // Include any trailing white space.
+ while (ascii_iswhite(line[i])) {
+ i++;
+ }
+
+ if (include_space) {
+ result = i;
+ }
+
+ // If this comment doesn't nest, stop here.
+ got_com = true;
+ if (vim_strchr(part_buf, COM_NEST) == NULL) {
+ break;
+ }
+ }
+ return result;
+}
+
+/// Return the offset at which the last comment in line starts. If there is no
+/// comment in the whole line, -1 is returned.
+///
+/// When "flags" is not null, it is set to point to the flags describing the
+/// recognized comment leader.
+int get_last_leader_offset(char_u *line, char_u **flags)
+{
+ int result = -1;
+ int i, j;
+ int lower_check_bound = 0;
+ char_u *string;
+ char_u *com_leader;
+ char_u *com_flags;
+ char_u *list;
+ int found_one;
+ char_u part_buf[COM_MAX_LEN]; // buffer for one option part
+
+ // Repeat to match several nested comment strings.
+ i = (int)STRLEN(line);
+ while (--i >= lower_check_bound) {
+ // scan through the 'comments' option for a match
+ found_one = false;
+ for (list = curbuf->b_p_com; *list;) {
+ char_u *flags_save = list;
+
+ // Get one option part into part_buf[]. Advance list to next one.
+ // put string at start of string.
+ (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
+ string = vim_strchr(part_buf, ':');
+ if (string == NULL) { // If everything is fine, this cannot actually
+ // happen.
+ continue;
+ }
+ *string++ = NUL; // Isolate flags from string.
+ com_leader = string;
+
+ // Line contents and string must match.
+ // When string starts with white space, must have some white space
+ // (but the amount does not need to match, there might be a mix of
+ // TABs and spaces).
+ if (ascii_iswhite(string[0])) {
+ if (i == 0 || !ascii_iswhite(line[i - 1])) {
+ continue;
+ }
+ while (ascii_iswhite(*string)) {
+ string++;
+ }
+ }
+ for (j = 0; string[j] != NUL && string[j] == line[i + j]; j++) {
+ // do nothing
+ }
+ if (string[j] != NUL) {
+ continue;
+ }
+
+ // When 'b' flag used, there must be white space or an
+ // end-of-line after the string in the line.
+ if (vim_strchr(part_buf, COM_BLANK) != NULL
+ && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) {
+ continue;
+ }
+
+ if (vim_strchr(part_buf, COM_MIDDLE) != NULL) {
+ // For a middlepart comment, only consider it to match if
+ // everything before the current position in the line is
+ // whitespace. Otherwise we would think we are inside a
+ // comment if the middle part appears somewhere in the middle
+ // of the line. E.g. for C the "*" appears often.
+ for (j = 0; j <= i && ascii_iswhite(line[j]); j++) {
+ }
+ if (j < i) {
+ continue;
+ }
+ }
+
+ // We have found a match, stop searching.
+ found_one = true;
+
+ if (flags) {
+ *flags = flags_save;
+ }
+ com_flags = flags_save;
+
+ break;
+ }
+
+ if (found_one) {
+ char_u part_buf2[COM_MAX_LEN]; // buffer for one option part
+ int len1, len2, off;
+
+ result = i;
+ // If this comment nests, continue searching.
+ if (vim_strchr(part_buf, COM_NEST) != NULL) {
+ continue;
+ }
+
+ lower_check_bound = i;
+
+ // Let's verify whether the comment leader found is a substring
+ // of other comment leaders. If it is, let's adjust the
+ // lower_check_bound so that we make sure that we have determined
+ // the comment leader correctly.
+
+ while (ascii_iswhite(*com_leader)) {
+ com_leader++;
+ }
+ len1 = (int)STRLEN(com_leader);
+
+ for (list = curbuf->b_p_com; *list;) {
+ char_u *flags_save = list;
+
+ (void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ",");
+ if (flags_save == com_flags) {
+ continue;
+ }
+ string = vim_strchr(part_buf2, ':');
+ string++;
+ while (ascii_iswhite(*string)) {
+ string++;
+ }
+ len2 = (int)STRLEN(string);
+ if (len2 == 0) {
+ continue;
+ }
+
+ // Now we have to verify whether string ends with a substring
+ // beginning the com_leader.
+ for (off = (len2 > i ? i : len2); off > 0 && off + len1 > len2;) {
+ off--;
+ if (!STRNCMP(string + off, com_leader, len2 - off)) {
+ if (i - off < lower_check_bound) {
+ lower_check_bound = i - off;
+ }
+ }
+ }
+ }
+ }
+ }
+ return result;
+}
diff --git a/src/nvim/change.h b/src/nvim/change.h
index e1a1bfba17..e7c8a2b031 100644
--- a/src/nvim/change.h
+++ b/src/nvim/change.h
@@ -4,6 +4,13 @@
#include "nvim/buffer_defs.h" // for buf_T
#include "nvim/pos.h" // for linenr_T
+// flags for open_line()
+#define OPENLINE_DELSPACES 1 // delete spaces after cursor
+#define OPENLINE_DO_COM 2 // format comments
+#define OPENLINE_KEEPTRAIL 4 // keep trailing spaces
+#define OPENLINE_MARKFIX 8 // fix mark positions
+#define OPENLINE_COM_LIST 16 // format comments with list/2nd line indent
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "change.h.generated.h"
#endif
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index a662f3a951..cd5134fe5f 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -10,7 +10,6 @@
#include "nvim/event/socket.h"
#include "nvim/fileio.h"
#include "nvim/lua/executor.h"
-#include "nvim/misc1.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/os/shell.h"
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index eb0903b594..599d662993 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -21,7 +21,6 @@
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/os_unix.h"
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 4e1d7f9d78..6e2c6232d7 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -14,7 +14,6 @@
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/plines.h"
#include "nvim/screen.h"
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 1f8acd8c79..0233b3a5ab 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -29,7 +29,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
@@ -39,6 +38,7 @@
#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/undo.h"
+#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/window.h"
#include "xdiff/xdiff.h"
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index d1dd9b8309..8eda173cac 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -20,7 +20,6 @@
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/normal.h"
#include "nvim/os/input.h"
#include "nvim/screen.h"
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 9bfb8a9d4a..2e3eec3642 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -35,7 +35,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
@@ -644,7 +643,10 @@ static int insert_check(VimState *state)
update_curswant();
s->old_topline = curwin->w_topline;
s->old_topfill = curwin->w_topfill;
- s->lastc = s->c; // remember previous char for CTRL-D
+
+ if (s->c != K_EVENT) {
+ s->lastc = s->c; // remember previous char for CTRL-D
+ }
// After using CTRL-G U the next cursor key will not break undo.
if (dont_sync_undo == kNone) {
@@ -832,6 +834,16 @@ static int insert_execute(VimState *state, int key)
return insert_handle_key(s);
}
+
+/// Return true when need to go to Insert mode because of 'insertmode'.
+///
+/// Don't do this when still processing a command or a mapping.
+/// Don't do this when inside a ":normal" command.
+bool goto_im(void)
+{
+ return p_im && stuff_empty() && typebuf_typed();
+}
+
static int insert_handle_key(InsertState *s)
{
// The big switch to handle a character in insert mode.
@@ -1676,7 +1688,7 @@ static void init_prompt(int cmdchar_todo)
// Insert always starts after the prompt, allow editing text after it.
if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)STRLEN(prompt)) {
Insstart.lnum = curwin->w_cursor.lnum;
- Insstart.col = STRLEN(prompt);
+ Insstart.col = (colnr_T)STRLEN(prompt);
Insstart_orig = Insstart;
Insstart_textlen = Insstart.col;
Insstart_blank_vcol = MAXCOL;
@@ -1687,7 +1699,7 @@ static void init_prompt(int cmdchar_todo)
coladvance(MAXCOL);
}
if (curwin->w_cursor.col < (colnr_T)STRLEN(prompt)) {
- curwin->w_cursor.col = STRLEN(prompt);
+ curwin->w_cursor.col = (colnr_T)STRLEN(prompt);
}
// Make sure the cursor is in a valid position.
check_cursor();
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 85e81ee975..86384bc5b2 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -35,7 +35,6 @@
#include "nvim/lua/executor.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/ops.h"
#include "nvim/option.h"
@@ -303,6 +302,31 @@ const list_T *eval_msgpack_type_lists[] = {
[kMPExt] = NULL,
};
+dict_T *get_v_event(save_v_event_T *sve)
+{
+ dict_T *v_event = get_vim_var_dict(VV_EVENT);
+
+ if (v_event->dv_hashtab.ht_used > 0) {
+ // recursive use of v:event, save, make empty and restore later
+ sve->sve_did_save = true;
+ sve->sve_hashtab = v_event->dv_hashtab;
+ hash_init(&v_event->dv_hashtab);
+ } else {
+ sve->sve_did_save = false;
+ }
+ return v_event;
+}
+
+void restore_v_event(dict_T *v_event, save_v_event_T *sve)
+{
+ tv_dict_free_contents(v_event);
+ if (sve->sve_did_save) {
+ v_event->dv_hashtab = sve->sve_hashtab;
+ } else {
+ hash_init(&v_event->dv_hashtab);
+ }
+}
+
// Return "n1" divided by "n2", taking care of dividing by zero.
varnumber_T num_divide(varnumber_T n1, varnumber_T n2)
FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 3b3a68bd29..a9ec5d47a6 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -193,6 +193,13 @@ extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1];
#undef LAST_MSGPACK_TYPE
+// Struct passed to get_v_event() and restore_v_event().
+typedef struct {
+ bool sve_did_save;
+ hashtab_T sve_hashtab;
+} save_v_event_T;
+
+
/// trans_function_name() flags
typedef enum {
TFN_INT = 1, ///< May use internal function name
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 9a76b67de6..e445a08227 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -133,6 +133,7 @@ return {
foldtext={},
foldtextresult={args=1, base=1},
foreground={},
+ fullcommand={args=1, base=1},
funcref={args={1, 3}, base=1},
['function']={args={1, 3}, base=1},
garbagecollect={args={0, 1}},
@@ -277,6 +278,7 @@ return {
readfile={args={1, 3}, base=1},
reg_executing={},
reg_recording={},
+ reg_recorded={},
reltime={args={0, 2}, base=1},
reltimefloat={args=1, base=1},
reltimestr={args=1, base=1},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 33ca4016cf..32026282cf 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -33,12 +33,12 @@
#include "nvim/if_cscope.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
+#include "nvim/input.h"
#include "nvim/lua/executor.h"
#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/math.h"
#include "nvim/memline.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/msgpack_rpc/channel.h"
@@ -1082,15 +1082,13 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Return the current directory
cwd = xmalloc(MAXPATHL);
- if (cwd != NULL) {
- if (os_dirname(cwd, MAXPATHL) != FAIL) {
+ if (os_dirname(cwd, MAXPATHL) != FAIL) {
#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(cwd);
+ slash_adjust(cwd);
#endif
- rettv->vval.v_string = vim_strsave(cwd);
- }
- xfree(cwd);
+ rettv->vval.v_string = vim_strsave(cwd);
}
+ xfree(cwd);
if (curwin->w_localdir != NULL) {
scope = kCdScopeWindow;
@@ -3920,34 +3918,46 @@ static void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_qf_loc_list(true, NULL, &argvars[0], rettv);
}
-/// "getreg()" function
-static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+/// Common between getreg(), getreginfo() and getregtype(): get the register
+/// name from the first argument.
+/// Returns zero on error.
+static int getreg_get_regname(typval_T *argvars)
{
- const char *strregname;
- int arg2 = false;
- bool return_list = false;
- bool error = false;
+ const char_u *strregname;
if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = tv_get_string_chk(&argvars[0]);
- error = strregname == NULL;
- if (argvars[1].v_type != VAR_UNKNOWN) {
- arg2 = tv_get_number_chk(&argvars[1], &error);
- if (!error && argvars[2].v_type != VAR_UNKNOWN) {
- return_list = tv_get_number_chk(&argvars[2], &error);
- }
+ strregname = (const char_u *)tv_get_string_chk(&argvars[0]);
+ if (strregname == NULL) { // type error; errmsg already given
+ return 0;
}
} else {
- strregname = _(get_vim_var_str(VV_REG));
+ // Default to v:register
+ strregname = get_vim_var_str(VV_REG);
}
- if (error) {
+ return *strregname == 0 ? '"' : *strregname;
+}
+
+/// "getreg()" function
+static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ int arg2 = false;
+ bool return_list = false;
+
+ int regname = getreg_get_regname(argvars);
+ if (regname == 0) {
return;
}
- int regname = (uint8_t)(strregname == NULL ? '"' : *strregname);
- if (regname == 0) {
- regname = '"';
+ if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) {
+ bool error = false;
+ arg2 = (int)tv_get_number_chk(&argvars[1], &error);
+ if (!error && argvars[2].v_type != VAR_UNKNOWN) {
+ return_list = (bool)tv_get_number_chk(&argvars[2], &error);
+ }
+ if (error) {
+ return;
+ }
}
if (return_list) {
@@ -3964,28 +3974,16 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "getregtype()" function
- */
+/// "getregtype()" function
static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- const char *strregname;
-
- if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = tv_get_string_chk(&argvars[0]);
- if (strregname == NULL) { // Type error; errmsg already given.
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- return;
- }
- } else {
- // Default to v:register.
- strregname = _(get_vim_var_str(VV_REG));
- }
+ // on error return an empty string
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
- int regname = (uint8_t)(strregname == NULL ? '"' : *strregname);
+ int regname = getreg_get_regname(argvars);
if (regname == 0) {
- regname = '"';
+ return;
}
colnr_T reglen = 0;
@@ -7333,18 +7331,12 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "getreginfo()" function
static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- const char *strregname;
- if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = tv_get_string_chk(&argvars[0]);
- if (strregname == NULL) {
- return;
- }
- } else {
- strregname = (const char *)get_vim_var_str(VV_REG);
+ int regname = getreg_get_regname(argvars);
+ if (regname == 0) {
+ return;
}
- int regname = (strregname == NULL ? '"' : *strregname);
- if (regname == 0 || regname == '@') {
+ if (regname == '@') {
regname = '"';
}
@@ -7398,6 +7390,11 @@ static void f_reg_recording(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return_register(reg_recording, rettv);
}
+static void f_reg_recorded(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ return_register(reg_recorded, rettv);
+}
+
/// list2proftime - convert a List to proftime_T
///
/// @param arg The input list, must be of type VAR_LIST and have
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 3e37e8cbb6..11bbaaed9c 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -28,11 +28,10 @@
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/os/input.h"
#include "nvim/pos.h"
#include "nvim/types.h"
#include "nvim/vim.h"
-// TODO(ZyX-I): Move line_breakcheck out of misc1
-#include "nvim/misc1.h" // For line_breakcheck
#include "nvim/os/fileio.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 9478a8441b..eb241eb8ae 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -17,7 +17,6 @@
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/lua/executor.h"
-#include "nvim/misc1.h"
#include "nvim/os/input.h"
#include "nvim/regexp.h"
#include "nvim/search.h"
diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c
index f070c8179f..3c43d1f98d 100644
--- a/src/nvim/event/rstream.c
+++ b/src/nvim/event/rstream.c
@@ -12,7 +12,7 @@
#include "nvim/event/rstream.h"
#include "nvim/log.h"
#include "nvim/memory.h"
-#include "nvim/misc1.h"
+#include "nvim/main.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index f95fe84f69..4965eb9c20 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -39,6 +39,7 @@
#include "nvim/getchar.h"
#include "nvim/highlight.h"
#include "nvim/indent.h"
+#include "nvim/input.h"
#include "nvim/log.h"
#include "nvim/main.h"
#include "nvim/mark.h"
@@ -46,7 +47,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
@@ -4584,6 +4584,9 @@ void ex_global(exarg_T *eap)
// a match on this line?
match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
(colnr_T)0, NULL, NULL);
+ if (regmatch.regprog == NULL) {
+ break; // re-compiling regprog failed
+ }
if ((type == 'g' && match) || (type == 'v' && !match)) {
ml_setmarked(lnum);
ndone++;
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 449e6f7bf5..33f9477608 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -35,12 +35,12 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/os/fs_defs.h"
+#include "nvim/os/input.h"
#include "nvim/os/shell.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 77cd50ecb7..230cbd4b60 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -39,6 +39,7 @@
#include "nvim/globals.h"
#include "nvim/hardcopy.h"
#include "nvim/if_cscope.h"
+#include "nvim/input.h"
#include "nvim/keymap.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
@@ -48,7 +49,6 @@
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
@@ -68,6 +68,7 @@
#include "nvim/spell.h"
#include "nvim/spellfile.h"
#include "nvim/strings.h"
+#include "nvim/state.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
#include "nvim/terminal.h"
@@ -2899,6 +2900,31 @@ int cmd_exists(const char *const name)
return ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1);
}
+// "fullcommand" function
+void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ exarg_T ea;
+ char_u *name = argvars[0].vval.v_string;
+
+ while (name[0] != NUL && name[0] == ':') {
+ name++;
+ }
+ name = skip_range(name, NULL);
+
+ rettv->v_type = VAR_STRING;
+
+ ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
+ ea.cmdidx = (cmdidx_T)0;
+ char_u *p = find_command(&ea, NULL);
+ if (p == NULL || ea.cmdidx == CMD_SIZE) {
+ return;
+ }
+
+ rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx)
+ ? get_user_commands(NULL, ea.useridx)
+ : cmdnames[ea.cmdidx].cmd_name);
+}
+
/// This is all pretty much copied from do_one_cmd(), with all the extra stuff
/// we don't need/want deleted. Maybe this could be done better if we didn't
/// repeat all this stuff. The only problem is that they may not stay
@@ -7806,17 +7832,21 @@ bool changedir_func(char_u *new_dir, CdScope scope)
prev_dir = pdir;
}
+ // For UNIX ":cd" means: go to home directory.
+ // On other systems too if 'cdhome' is set.
#if defined(UNIX)
- // On Unix ":cd" means: go to home directory.
if (*new_dir == NUL) {
+#else
+ if (*new_dir == NUL && p_cdh) {
+#endif
// Use NameBuff for home directory name.
expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
new_dir = NameBuff;
}
-#endif
- if (vim_chdir(new_dir) == 0) {
- bool dir_differs = pdir == NULL || pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
+ bool dir_differs = new_dir == NULL || pdir == NULL
+ || pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
+ if (new_dir != NULL && (!dir_differs || vim_chdir(new_dir) == 0)) {
post_chdir(scope, dir_differs);
retval = true;
} else {
@@ -7832,9 +7862,9 @@ void ex_cd(exarg_T *eap)
{
char_u *new_dir;
new_dir = eap->arg;
-#if !defined(UNIX) && !defined(VMS)
- // for non-UNIX ":cd" means: print current directory
- if (*new_dir == NUL) {
+#if !defined(UNIX)
+ // for non-UNIX ":cd" means: print current directory unless 'cdhome' is set
+ if (*new_dir == NUL && !p_cdh) {
ex_pwd(NULL);
} else
#endif
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index 7ec4fad277..a302d4a3c5 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -2,6 +2,7 @@
#define NVIM_EX_DOCMD_H
#include "nvim/ex_cmds_defs.h"
+#include "nvim/eval/funcs.h"
#include "nvim/globals.h"
// flags for do_cmdline()
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 475f22d061..ba2238ace2 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -48,7 +48,6 @@
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/ops.h"
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index f80a63560c..a37cad9f2d 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -28,7 +28,6 @@
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/keymap.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index 5953a574f3..1884dd49c5 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -57,7 +57,6 @@
#include "nvim/globals.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/option.h"
#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
@@ -1668,14 +1667,19 @@ int vim_chdirfile(char_u *fname, CdCause cause)
NameBuff[0] = NUL;
}
- if (os_chdir(dir) == 0) {
- if (cause != kCdCauseOther && pathcmp(dir, (char *)NameBuff, -1) != 0) {
- do_autocmd_dirchanged(dir, kCdScopeWindow, cause);
- }
- } else {
+ if (pathcmp(dir, (char *)NameBuff, -1) == 0) {
+ // nothing to do
+ return OK;
+ }
+
+ if (os_chdir(dir) != 0) {
return FAIL;
}
+ if (cause != kCdCauseOther) {
+ do_autocmd_dirchanged(dir, kCdScopeWindow, cause);
+ }
+
return OK;
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 24428c2d9a..f8cf341836 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -30,12 +30,12 @@
#include "nvim/getchar.h"
#include "nvim/hashtab.h"
#include "nvim/iconv.h"
+#include "nvim/input.h"
#include "nvim/mbyte.h"
#include "nvim/memfile.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 4a8be7a31b..b1d4321d4c 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -28,10 +28,10 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/os/input.h"
#include "nvim/plines.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
diff --git a/src/nvim/generators/gen_char_blob.lua b/src/nvim/generators/gen_char_blob.lua
index a7dad50d48..3ec1ff2caf 100644
--- a/src/nvim/generators/gen_char_blob.lua
+++ b/src/nvim/generators/gen_char_blob.lua
@@ -1,12 +1,26 @@
if arg[1] == '--help' then
print('Usage:')
- print(' '..arg[0]..' target source varname [source varname]...')
+ print(' '..arg[0]..' [-c] target source varname [source varname]...')
print('')
print('Generates C file with big uint8_t blob.')
print('Blob will be stored in a static const array named varname.')
os.exit()
end
+-- Recognized options:
+-- -c compile Lua bytecode
+local options = {}
+
+while true do
+ local opt = string.match(arg[1], "^-(%w)")
+ if not opt then
+ break
+ end
+
+ options[opt] = true
+ table.remove(arg, 1)
+end
+
assert(#arg >= 3 and (#arg - 1) % 2 == 0)
local target_file = arg[1] or error('Need a target file')
@@ -14,6 +28,7 @@ local target = io.open(target_file, 'w')
target:write('#include <stdint.h>\n\n')
+local warn_on_missing_compiler = true
local varnames = {}
for argi = 2, #arg, 2 do
local source_file = arg[argi]
@@ -23,11 +38,26 @@ for argi = 2, #arg, 2 do
end
varnames[varname] = source_file
- local source = io.open(source_file, 'r')
- or error(string.format("source_file %q doesn't exist", source_file))
-
target:write(('static const uint8_t %s[] = {\n'):format(varname))
+ local output
+ if options.c then
+ local luac = os.getenv("LUAC_PRG")
+ if luac and luac ~= "" then
+ output = io.popen(luac:format(source_file), "r"):read("*a")
+ elseif warn_on_missing_compiler then
+ print("LUAC_PRG is missing, embedding raw source")
+ warn_on_missing_compiler = false
+ end
+ end
+
+ if not output then
+ local source = io.open(source_file, "r")
+ or error(string.format("source_file %q doesn't exist", source_file))
+ output = source:read("*a")
+ source:close()
+ end
+
local num_bytes = 0
local MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line
target:write(' ')
@@ -41,19 +71,13 @@ for argi = 2, #arg, 2 do
end
end
- for line in source:lines() do
- for i = 1, string.len(line) do
- local byte = line:byte(i)
- assert(byte ~= 0)
- target:write(string.format(' %3u,', byte))
- increase_num_bytes()
- end
- target:write(string.format(' %3u,', string.byte('\n', 1)))
+ for i = 1, string.len(output) do
+ local byte = output:byte(i)
+ target:write(string.format(' %3u,', byte))
increase_num_bytes()
end
- target:write(' 0};\n')
- source:close()
+ target:write(' 0};\n')
end
target:close()
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 27e8cb36af..6b1150cefa 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -29,6 +29,7 @@
#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/input.h"
#include "nvim/keymap.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
@@ -36,7 +37,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/ops.h"
@@ -458,6 +458,15 @@ void flush_buffers(flush_buffers_T flush_typeahead)
}
}
+/// flush map and typeahead buffers and give a warning for an error
+void beep_flush(void)
+{
+ if (emsg_silent == 0) {
+ flush_buffers(FLUSH_MINIMAL);
+ vim_beep(BO_ERROR);
+ }
+}
+
/*
* The previous contents of the redo buffer is kept in old_redobuffer.
* This is used for the CTRL-O <.> command in insert mode.
@@ -1885,7 +1894,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// buffer right here. Otherwise, use the mapping (loop around).
if (mp == NULL) {
*keylenp = keylen;
- return map_result_get; // got character, break for loop
+ return map_result_get; // get character from typeahead
} else {
keylen = mp_match_len;
}
@@ -2157,7 +2166,7 @@ static int vgetorpeek(bool advance)
KeyNoremap = typebuf.tb_noremap[typebuf.tb_off];
del_typebuf(1, 0);
}
- break;
+ break; // got character, break the for loop
}
// not enough characters, get more
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index b2422fd531..697d4b11a7 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -202,7 +202,6 @@ EXTERN bool msg_did_scroll INIT(= false);
EXTERN char_u *keep_msg INIT(= NULL); // msg to be shown after redraw
EXTERN int keep_msg_attr INIT(= 0); // highlight attr for keep_msg
-EXTERN bool keep_msg_more INIT(= false); // keep_msg was set by msgmore()
EXTERN bool need_fileinfo INIT(= false); // do fileinfo() after redraw
EXTERN int msg_scroll INIT(= false); // msg_start() will scroll
EXTERN bool msg_didout INIT(= false); // msg_outstr() was used in line
@@ -633,6 +632,7 @@ EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p.
EXTERN int reg_recording INIT(= 0); // register for recording or zero
EXTERN int reg_executing INIT(= 0); // register being executed or zero
+EXTERN int reg_recorded INIT(= 0); // last recorded register or zero
EXTERN int no_mapping INIT(= false); // currently no mapping allowed
EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed
@@ -997,6 +997,8 @@ EXTERN char e_non_empty_string_required[] INIT(= N_("E1142: Non-empty string req
EXTERN char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cannot define autocommands for ALL events"));
+EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long"));
+
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP"));
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index d4d53c4126..50a03e0c02 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -65,6 +65,8 @@ typedef enum {
HLF_LNA, // LineNrAbove
HLF_LNB, // LineNrBelow
HLF_CLN, // current line number when 'cursorline' is set
+ HLF_CLS, // current line sign column
+ HLF_CLF, // current line fold
HLF_R, // return to continue message and yes/no questions
HLF_S, // status lines
HLF_SNC, // status lines of not-current windows
@@ -122,6 +124,8 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_LNA] = "LineNrAbove",
[HLF_LNB] = "LineNrBelow",
[HLF_CLN] = "CursorLineNr",
+ [HLF_CLS] = "CursorLineSign",
+ [HLF_CLF] = "CursorLineFold",
[HLF_R] = "Question",
[HLF_S] = "StatusLine",
[HLF_SNC] = "StatusLineNC",
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index f49aff6643..8cc5bc2436 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -17,7 +17,6 @@
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 3e3e07e9d6..faa9b38cf7 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -8,7 +8,6 @@
#include "nvim/vim.h"
#include "nvim/ascii.h"
-#include "nvim/misc1.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
diff --git a/src/nvim/input.c b/src/nvim/input.c
new file mode 100644
index 0000000000..2f7c5c2c16
--- /dev/null
+++ b/src/nvim/input.c
@@ -0,0 +1,255 @@
+// 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
+
+// input.c: high level functions for prompting the user or input
+// like yes/no or number prompts.
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "nvim/func_attr.h"
+#include "nvim/getchar.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
+#include "nvim/input.h"
+#include "nvim/mouse.h"
+#include "nvim/os/input.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "input.c.generated.h"
+#endif
+
+/// Ask for a reply from the user, 'y' or 'n'
+///
+/// No other characters are accepted, the message is repeated until a valid
+/// reply is entered or <C-c> is hit.
+///
+/// @param[in] str Prompt: question to ask user. Is always followed by
+/// " (y/n)?".
+/// @param[in] direct Determines what function to use to get user input. If
+/// true then ui_inchar() will be used, otherwise vgetc().
+/// I.e. when direct is true then characters are obtained
+/// directly from the user without buffers involved.
+///
+/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt.
+int ask_yesno(const char *const str, const bool direct)
+{
+ const int save_State = State;
+
+ no_wait_return++;
+ State = CONFIRM; // Mouse behaves like with :confirm.
+ setmouse(); // Disable mouse in xterm.
+ no_mapping++;
+
+ int r = ' ';
+ while (r != 'y' && r != 'n') {
+ // Same highlighting as for wait_return.
+ smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str);
+ if (direct) {
+ r = get_keystroke(NULL);
+ } else {
+ r = plain_vgetc();
+ }
+ if (r == Ctrl_C || r == ESC) {
+ r = 'n';
+ }
+ msg_putchar(r); // Show what you typed.
+ ui_flush();
+ }
+ no_wait_return--;
+ State = save_State;
+ setmouse();
+ no_mapping--;
+
+ return r;
+}
+
+/// Get a key stroke directly from the user.
+///
+/// Ignores mouse clicks and scrollbar events, except a click for the left
+/// button (used at the more prompt).
+/// Doesn't use vgetc(), because it syncs undo and eats mapped characters.
+/// Disadvantage: typeahead is ignored.
+/// Translates the interrupt character for unix to ESC.
+int get_keystroke(MultiQueue *events)
+{
+ char_u *buf = NULL;
+ int buflen = 150;
+ int maxlen;
+ int len = 0;
+ int n;
+ int save_mapped_ctrl_c = mapped_ctrl_c;
+ int waited = 0;
+
+ mapped_ctrl_c = 0; // mappings are not used here
+ for (;;) {
+ // flush output before waiting
+ ui_flush();
+ // Leave some room for check_termcode() to insert a key code into (max
+ // 5 chars plus NUL). And fix_input_buffer() can triple the number of
+ // bytes.
+ maxlen = (buflen - 6 - len) / 3;
+ if (buf == NULL) {
+ buf = xmalloc((size_t)buflen);
+ } else if (maxlen < 10) {
+ // Need some more space. This might happen when receiving a long
+ // escape sequence.
+ buflen += 100;
+ buf = xrealloc(buf, (size_t)buflen);
+ maxlen = (buflen - 6 - len) / 3;
+ }
+
+ // First time: blocking wait. Second time: wait up to 100ms for a
+ // terminal code to complete.
+ n = os_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0, events);
+ if (n > 0) {
+ // Replace zero and CSI by a special key code.
+ n = fix_input_buffer(buf + len, n);
+ len += n;
+ waited = 0;
+ } else if (len > 0) {
+ waited++; // keep track of the waiting time
+ }
+ if (n > 0) { // found a termcode: adjust length
+ len = n;
+ }
+ if (len == 0) { // nothing typed yet
+ continue;
+ }
+
+ // Handle modifier and/or special key code.
+ n = buf[0];
+ if (n == K_SPECIAL) {
+ n = TO_SPECIAL(buf[1], buf[2]);
+ if (buf[1] == KS_MODIFIER
+ || n == K_IGNORE
+ || (is_mouse_key(n) && n != K_LEFTMOUSE)) {
+ if (buf[1] == KS_MODIFIER) {
+ mod_mask = buf[2];
+ }
+ len -= 3;
+ if (len > 0) {
+ memmove(buf, buf + 3, (size_t)len);
+ }
+ continue;
+ }
+ break;
+ }
+ if (MB_BYTE2LEN(n) > len) {
+ // more bytes to get.
+ continue;
+ }
+ buf[len >= buflen ? buflen - 1 : len] = NUL;
+ n = utf_ptr2char(buf);
+ break;
+ }
+ xfree(buf);
+
+ mapped_ctrl_c = save_mapped_ctrl_c;
+ return n;
+}
+
+/// Get a number from the user.
+/// When "mouse_used" is not NULL allow using the mouse.
+///
+/// @param colon allow colon to abort
+int get_number(int colon, int *mouse_used)
+{
+ int n = 0;
+ int c;
+ int typed = 0;
+
+ if (mouse_used != NULL) {
+ *mouse_used = false;
+ }
+
+ // When not printing messages, the user won't know what to type, return a
+ // zero (as if CR was hit).
+ if (msg_silent != 0) {
+ return 0;
+ }
+
+ no_mapping++;
+ for (;;) {
+ ui_cursor_goto(msg_row, msg_col);
+ c = safe_vgetc();
+ if (ascii_isdigit(c)) {
+ n = n * 10 + c - '0';
+ msg_putchar(c);
+ typed++;
+ } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) {
+ if (typed > 0) {
+ msg_puts("\b \b");
+ typed--;
+ }
+ n /= 10;
+ } else if (mouse_used != NULL && c == K_LEFTMOUSE) {
+ *mouse_used = true;
+ n = mouse_row + 1;
+ break;
+ } else if (n == 0 && c == ':' && colon) {
+ stuffcharReadbuff(':');
+ if (!exmode_active) {
+ cmdline_row = msg_row;
+ }
+ skip_redraw = true; // skip redraw once
+ do_redraw = false;
+ break;
+ } else if (c == Ctrl_C || c == ESC || c == 'q') {
+ n = 0;
+ break;
+ } else if (c == CAR || c == NL) {
+ break;
+ }
+ }
+ no_mapping--;
+ return n;
+}
+
+/// Ask the user to enter a number.
+///
+/// When "mouse_used" is not NULL allow using the mouse and in that case return
+/// the line number.
+int prompt_for_number(int *mouse_used)
+{
+ int i;
+ int save_cmdline_row;
+ int save_State;
+
+ // When using ":silent" assume that <CR> was entered.
+ if (mouse_used != NULL) {
+ msg_puts(_("Type number and <Enter> or click with the mouse "
+ "(q or empty cancels): "));
+ } else {
+ msg_puts(_("Type number and <Enter> (q or empty cancels): "));
+ }
+
+ // Set the state such that text can be selected/copied/pasted and we still
+ // get mouse events.
+ save_cmdline_row = cmdline_row;
+ cmdline_row = 0;
+ save_State = State;
+ State = ASKMORE; // prevents a screen update when using a timer
+ // May show different mouse shape.
+ setmouse();
+
+ i = get_number(true, mouse_used);
+ if (KeyTyped) {
+ // don't call wait_return() now
+ if (msg_row > 0) {
+ cmdline_row = msg_row - 1;
+ }
+ need_wait_return = false;
+ msg_didany = false;
+ msg_didout = false;
+ } else {
+ cmdline_row = save_cmdline_row;
+ }
+ State = save_State;
+ // May need to restore mouse shape.
+ setmouse();
+
+ return i;
+}
diff --git a/src/nvim/input.h b/src/nvim/input.h
new file mode 100644
index 0000000000..7975f21215
--- /dev/null
+++ b/src/nvim/input.h
@@ -0,0 +1,9 @@
+#ifndef NVIM_INPUT_H
+#define NVIM_INPUT_H
+
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "input.h.generated.h"
+#endif
+#endif // NVIM_INPUT_H
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index a899ca63ac..107ff22913 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -31,7 +31,6 @@
#include "nvim/map.h"
#include "nvim/memline.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/os.h"
#include "nvim/screen.h"
@@ -405,9 +404,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
{
const char *code = (char *)&shared_module[0];
- if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/shared.lua")
+ if (luaL_loadbuffer(lstate, code, sizeof(shared_module) - 1, "@vim/shared.lua")
|| nlua_pcall(lstate, 0, 0)) {
- nlua_error(lstate, _("E5106: Error while creating shared module: %.*s"));
+ nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n"));
return 1;
}
}
@@ -417,18 +416,18 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_getfield(lstate, -1, "loaded"); // [package, loaded]
const char *code = (char *)&inspect_module[0];
- if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/inspect.lua")
+ if (luaL_loadbuffer(lstate, code, sizeof(inspect_module) - 1, "@vim/inspect.lua")
|| nlua_pcall(lstate, 0, 1)) {
- nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s"));
+ nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s\n"));
return 1;
}
// [package, loaded, inspect]
lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded]
code = (char *)&lua_F_module[0];
- if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/F.lua")
+ if (luaL_loadbuffer(lstate, code, sizeof(lua_F_module) - 1, "@vim/F.lua")
|| nlua_pcall(lstate, 0, 1)) {
- nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s"));
+ nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s\n"));
return 1;
}
// [package, loaded, module]
@@ -439,9 +438,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
{
const char *code = (char *)&vim_module[0];
- if (luaL_loadbuffer(lstate, code, strlen(code), "@vim.lua")
+ if (luaL_loadbuffer(lstate, code, sizeof(vim_module) - 1, "@vim.lua")
|| nlua_pcall(lstate, 0, 0)) {
- nlua_error(lstate, _("E5106: Error while creating vim module: %.*s"));
+ nlua_error(lstate, _("E5106: Error while creating vim module: %.*s\n"));
return 1;
}
}
@@ -451,9 +450,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_getfield(lstate, -1, "loaded"); // [package, loaded]
const char *code = (char *)&lua_meta_module[0];
- if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/_meta.lua")
+ if (luaL_loadbuffer(lstate, code, sizeof(lua_meta_module) - 1, "@vim/_meta.lua")
|| nlua_pcall(lstate, 0, 1)) {
- nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s"));
+ nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s\n"));
return 1;
}
// [package, loaded, module]
diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c
new file mode 100644
index 0000000000..b84124bc19
--- /dev/null
+++ b/src/nvim/lua/spell.c
@@ -0,0 +1,99 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "nvim/spell.h"
+#include "nvim/vim.h"
+#include "nvim/lua/spell.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "lua/spell.c.generated.h"
+#endif
+
+int nlua_spell_check(lua_State *lstate)
+{
+ if (lua_gettop(lstate) < 1) {
+ return luaL_error(lstate, "Expected 1 argument");
+ }
+
+ if (lua_type(lstate, 1) != LUA_TSTRING) {
+ luaL_argerror(lstate, 1, "expected string");
+ }
+
+ const char *str = lua_tolstring(lstate, 1, NULL);
+
+ // spell.c requires that 'spell' is enabled, so we need to temporarily enable
+ // it before we can call spell functions.
+ const int wo_spell_save = curwin->w_p_spell;
+
+ if (!curwin->w_p_spell) {
+ did_set_spelllang(curwin);
+ curwin->w_p_spell = true;
+ }
+
+ // Check 'spelllang'
+ if (*curwin->w_s->b_p_spl == NUL) {
+ emsg(_(e_no_spell));
+ curwin->w_p_spell = wo_spell_save;
+ return 0;
+ }
+
+ hlf_T attr = HLF_COUNT;
+ size_t len = 0;
+ size_t pos = 0;
+ int capcol = -1;
+ int no_res = 0;
+ const char * result;
+
+ lua_createtable(lstate, 0, 0);
+
+ while (*str != NUL) {
+ attr = HLF_COUNT;
+ len = spell_check(curwin, (char_u *)str, &attr, &capcol, false);
+ assert(len <= INT_MAX);
+
+ if (attr != HLF_COUNT) {
+ lua_createtable(lstate, 3, 0);
+
+ lua_pushlstring(lstate, str, len);
+ lua_rawseti(lstate, -2, 1);
+
+ result = attr == HLF_SPB ? "bad" :
+ attr == HLF_SPR ? "rare" :
+ attr == HLF_SPL ? "local" :
+ attr == HLF_SPC ? "caps" :
+ NULL;
+
+ assert(result != NULL);
+
+ lua_pushstring(lstate, result);
+ lua_rawseti(lstate, -2, 2);
+
+ // +1 for 1-indexing
+ lua_pushinteger(lstate, (long)pos + 1);
+ lua_rawseti(lstate, -2, 3);
+
+ lua_rawseti(lstate, -2, ++no_res);
+ }
+
+ str += len;
+ pos += len;
+ capcol -= (int)len;
+ }
+
+ // Restore 'spell'
+ curwin->w_p_spell = wo_spell_save;
+ return 1;
+}
+
+static const luaL_Reg spell_functions[] = {
+ { "check", nlua_spell_check },
+ { NULL , NULL }
+};
+
+int luaopen_spell(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_register(L, NULL, spell_functions);
+ return 1;
+}
diff --git a/src/nvim/lua/spell.h b/src/nvim/lua/spell.h
new file mode 100644
index 0000000000..8f798a5191
--- /dev/null
+++ b/src/nvim/lua/spell.h
@@ -0,0 +1,12 @@
+#ifndef NVIM_LUA_SPELL_H
+#define NVIM_LUA_SPELL_H
+
+#include <lauxlib.h>
+#include <lua.h>
+#include <lualib.h>
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "lua/spell.h.generated.h"
+#endif
+
+#endif // NVIM_LUA_SPELL_H
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index b5553060a1..18a579ed0f 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -30,11 +30,11 @@
#include "nvim/lua/stdlib.h"
#include "nvim/lua/treesitter.h"
#include "nvim/lua/xdiff.h"
+#include "nvim/lua/spell.h"
#include "nvim/macros.h"
#include "nvim/map.h"
#include "nvim/memline.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/os.h"
#include "nvim/regexp.h"
@@ -231,8 +231,8 @@ static int nlua_str_utf_start(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
if (offset < 0 || offset > (intptr_t)s1_len) {
return luaL_error(lstate, "index out of range");
}
- int tail_offset = mb_head_off((char_u *)s1, (char_u *)s1 + (char_u)offset - 1);
- lua_pushinteger(lstate, tail_offset);
+ int head_offset = mb_head_off((char_u *)s1, (char_u *)s1 + offset - 1);
+ lua_pushinteger(lstate, head_offset);
return 1;
}
@@ -251,7 +251,7 @@ static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
if (offset < 0 || offset > (intptr_t)s1_len) {
return luaL_error(lstate, "index out of range");
}
- int tail_offset = mb_tail_off((char_u *)s1, (char_u *)s1 + (char_u)offset - 1);
+ int tail_offset = mb_tail_off((char_u *)s1, (char_u *)s1 + offset - 1);
lua_pushinteger(lstate, tail_offset);
return 1;
}
@@ -519,6 +519,10 @@ void nlua_state_add_stdlib(lua_State *const lstate)
lua_pushcfunction(lstate, &nlua_xdl_diff);
lua_setfield(lstate, -2, "diff");
+ // vim.spell
+ luaopen_spell(lstate);
+ lua_setfield(lstate, -2, "spell");
+
lua_cjson_new(lstate);
lua_setfield(lstate, -2, "json");
}
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 921bc883cf..cbd1f53727 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -39,7 +39,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
@@ -696,6 +695,50 @@ void getout(int exitval)
os_exit(exitval);
}
+/// Preserve files and exit.
+/// @note IObuff must contain a message.
+/// @note This may be called from deadly_signal() in a signal handler, avoid
+/// unsafe functions, such as allocating memory.
+void preserve_exit(void)
+ FUNC_ATTR_NORETURN
+{
+ // 'true' when we are sure to exit, e.g., after a deadly signal
+ static bool really_exiting = false;
+
+ // Prevent repeated calls into this method.
+ if (really_exiting) {
+ if (input_global_fd() >= 0) {
+ // normalize stream (#2598)
+ stream_set_blocking(input_global_fd(), true);
+ }
+ exit(2);
+ }
+
+ really_exiting = true;
+ // Ignore SIGHUP while we are already exiting. #9274
+ signal_reject_deadly();
+ mch_errmsg(IObuff);
+ mch_errmsg("\n");
+ ui_flush();
+
+ ml_close_notmod(); // close all not-modified buffers
+
+ FOR_ALL_BUFFERS(buf) {
+ if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) {
+ mch_errmsg("Vim: preserving files...\r\n");
+ ui_flush();
+ ml_sync_all(false, false, true); // preserve all swap files
+ break;
+ }
+ }
+
+ ml_close_all(false); // close all memfiles, without deleting
+
+ mch_errmsg("Vim: Finished.\r\n");
+
+ getout(1);
+}
+
/// Gets the integer value of a numeric command line argument if given,
/// such as '-o10'.
///
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 42117bc762..5eb209a6f6 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -42,13 +42,13 @@
#include "nvim/eval.h"
#include "nvim/fileio.h"
#include "nvim/func_attr.h"
+#include "nvim/getchar.h"
#include "nvim/iconv.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
@@ -1609,7 +1609,8 @@ void show_utf8(void)
msg((char *)IObuff);
}
-/// Return offset from "p" to the first byte of the character it points into.
+/// Return offset from "p" to the start of a character, including composing characters.
+/// "base" must be the start of the string, which must be NUL terminated.
/// If "p" points to the NUL at the end of the string return 0.
/// Returns 0 when already at the first byte of a character.
int utf_head_off(const char_u *base, const char_u *p)
@@ -1850,10 +1851,9 @@ int mb_off_next(char_u *base, char_u *p)
return i;
}
-/*
- * Return the offset from "p" to the last byte of the character it points
- * into. Can start anywhere in a stream of bytes.
- */
+/// Return the offset from "p" to the last byte of the character it points
+/// into. Can start anywhere in a stream of bytes.
+/// Composing characters are not included.
int mb_tail_off(char_u *base, char_u *p)
{
int i;
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 08202a6d5c..08521c0dc3 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -51,6 +51,7 @@
#include "nvim/fileio.h"
#include "nvim/func_attr.h"
#include "nvim/getchar.h"
+#include "nvim/input.h"
#include "nvim/main.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -58,7 +59,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
@@ -1841,6 +1841,17 @@ char_u *ml_get_pos(const pos_T *pos)
return ml_get_buf(curbuf, pos->lnum, false) + pos->col;
}
+/// get codepoint at pos. pos must be either valid or have col set to MAXCOL!
+int gchar_pos(pos_T *pos)
+ FUNC_ATTR_NONNULL_ARG(1)
+{
+ // When searching columns is sometimes put at the end of a line.
+ if (pos->col == MAXCOL) {
+ return NUL;
+ }
+ return utf_ptr2char(ml_get_pos(pos));
+}
+
/// Return a pointer to a line in a specific buffer
///
/// @param will_change true mark the buffer dirty (chars in the line will be changed)
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 3d621ebbb7..677ff8f522 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -17,7 +17,6 @@
#include "nvim/memfile.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/sign.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index d596b31062..ac4d52c392 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -22,7 +22,6 @@
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/strings.h"
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 8a6ac2decc..befca8c76b 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -23,12 +23,12 @@
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/highlight.h"
+#include "nvim/input.h"
#include "nvim/keymap.h"
#include "nvim/main.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/normal.h"
#include "nvim/ops.h"
@@ -76,6 +76,8 @@ static int msg_hist_len = 0;
static FILE *verbose_fd = NULL;
static int verbose_did_open = FALSE;
+bool keep_msg_more = false; // keep_msg was set by msgmore()
+
/*
* When writing messages to the screen, there are many different situations.
* A number of variables is used to remember the current state:
@@ -1298,6 +1300,49 @@ void set_keep_msg(char *s, int attr)
keep_msg_attr = attr;
}
+void msgmore(long n)
+{
+ long pn;
+
+ if (global_busy // no messages now, wait until global is finished
+ || !messaging()) { // 'lazyredraw' set, don't do messages now
+ return;
+ }
+
+ // We don't want to overwrite another important message, but do overwrite
+ // a previous "more lines" or "fewer lines" message, so that "5dd" and
+ // then "put" reports the last action.
+ if (keep_msg != NULL && !keep_msg_more) {
+ return;
+ }
+
+ if (n > 0) {
+ pn = n;
+ } else {
+ pn = -n;
+ }
+
+ if (pn > p_report) {
+ if (n > 0) {
+ vim_snprintf(msg_buf, MSG_BUF_LEN,
+ NGETTEXT("%ld more line", "%ld more lines", pn),
+ pn);
+ } else {
+ vim_snprintf(msg_buf, MSG_BUF_LEN,
+ NGETTEXT("%ld line less", "%ld fewer lines", pn),
+ pn);
+ }
+ if (got_int) {
+ xstrlcat(msg_buf, _(" (Interrupted)"), MSG_BUF_LEN);
+ }
+ if (msg(msg_buf)) {
+ set_keep_msg(msg_buf, 0);
+ keep_msg_more = true;
+ }
+ }
+}
+
+
void msg_ext_set_kind(const char *msg_kind)
{
// Don't change the label of an existing batch:
@@ -2054,7 +2099,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
msg_ext_last_attr = attr;
}
// Concat pieces with the same highlight
- size_t len = strnlen((char *)str, maxlen); // -V781
+ size_t len = STRNLEN(str, maxlen); // -V781
ga_concat_len(&msg_ext_last_chunk, (char *)str, len);
msg_ext_cur_len += len;
return;
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
deleted file mode 100644
index 872a2c58e3..0000000000
--- a/src/nvim/misc1.c
+++ /dev/null
@@ -1,1116 +0,0 @@
-// 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
-
-/*
- * misc1.c: functions that didn't seem to fit elsewhere
- */
-
-#include <assert.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <string.h>
-
-#include "nvim/ascii.h"
-#include "nvim/buffer.h"
-#include "nvim/buffer_updates.h"
-#include "nvim/charset.h"
-#include "nvim/cursor.h"
-#include "nvim/diff.h"
-#include "nvim/edit.h"
-#include "nvim/eval.h"
-#include "nvim/event/stream.h"
-#include "nvim/ex_cmds.h"
-#include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
-#include "nvim/fileio.h"
-#include "nvim/fold.h"
-#include "nvim/func_attr.h"
-#include "nvim/garray.h"
-#include "nvim/getchar.h"
-#include "nvim/indent.h"
-#include "nvim/indent_c.h"
-#include "nvim/main.h"
-#include "nvim/mbyte.h"
-#include "nvim/memline.h"
-#include "nvim/memory.h"
-#include "nvim/message.h"
-#include "nvim/misc1.h"
-#include "nvim/mouse.h"
-#include "nvim/move.h"
-#include "nvim/option.h"
-#include "nvim/os/input.h"
-#include "nvim/os/os.h"
-#include "nvim/os/shell.h"
-#include "nvim/os/signal.h"
-#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
-#include "nvim/quickfix.h"
-#include "nvim/regexp.h"
-#include "nvim/screen.h"
-#include "nvim/search.h"
-#include "nvim/state.h"
-#include "nvim/strings.h"
-#include "nvim/tag.h"
-#include "nvim/ui.h"
-#include "nvim/undo.h"
-#include "nvim/vim.h"
-#include "nvim/window.h"
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "misc1.c.generated.h"
-#endif
-// All user names (for ~user completion as done by shell).
-static garray_T ga_users = GA_EMPTY_INIT_VALUE;
-
-/*
- * get_leader_len() returns the length in bytes of the prefix of the given
- * string which introduces a comment. If this string is not a comment then
- * 0 is returned.
- * When "flags" is not NULL, it is set to point to the flags of the recognized
- * comment leader.
- * "backward" must be true for the "O" command.
- * If "include_space" is set, include trailing whitespace while calculating the
- * length.
- */
-int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_space)
-{
- int i, j;
- int result;
- int got_com = FALSE;
- int found_one;
- char_u part_buf[COM_MAX_LEN]; // buffer for one option part
- char_u *string; // pointer to comment string
- char_u *list;
- int middle_match_len = 0;
- char_u *prev_list;
- char_u *saved_flags = NULL;
-
- result = i = 0;
- while (ascii_iswhite(line[i])) { // leading white space is ignored
- ++i;
- }
-
- /*
- * Repeat to match several nested comment strings.
- */
- while (line[i] != NUL) {
- /*
- * scan through the 'comments' option for a match
- */
- found_one = FALSE;
- for (list = curbuf->b_p_com; *list;) {
- // Get one option part into part_buf[]. Advance "list" to next
- // one. Put "string" at start of string.
- if (!got_com && flags != NULL) {
- *flags = list; // remember where flags started
- }
- prev_list = list;
- (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
- string = vim_strchr(part_buf, ':');
- if (string == NULL) { // missing ':', ignore this part
- continue;
- }
- *string++ = NUL; // isolate flags from string
-
- // If we found a middle match previously, use that match when this
- // is not a middle or end.
- if (middle_match_len != 0
- && vim_strchr(part_buf, COM_MIDDLE) == NULL
- && vim_strchr(part_buf, COM_END) == NULL) {
- break;
- }
-
- // When we already found a nested comment, only accept further
- // nested comments.
- if (got_com && vim_strchr(part_buf, COM_NEST) == NULL) {
- continue;
- }
-
- // When 'O' flag present and using "O" command skip this one.
- if (backward && vim_strchr(part_buf, COM_NOBACK) != NULL) {
- continue;
- }
-
- // Line contents and string must match.
- // When string starts with white space, must have some white space
- // (but the amount does not need to match, there might be a mix of
- // TABs and spaces).
- if (ascii_iswhite(string[0])) {
- if (i == 0 || !ascii_iswhite(line[i - 1])) {
- continue; // missing white space
- }
- while (ascii_iswhite(string[0])) {
- ++string;
- }
- }
- for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j) {
- }
- if (string[j] != NUL) {
- continue; // string doesn't match
- }
- // When 'b' flag used, there must be white space or an
- // end-of-line after the string in the line.
- if (vim_strchr(part_buf, COM_BLANK) != NULL
- && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) {
- continue;
- }
-
- // We have found a match, stop searching unless this is a middle
- // comment. The middle comment can be a substring of the end
- // comment in which case it's better to return the length of the
- // end comment and its flags. Thus we keep searching with middle
- // and end matches and use an end match if it matches better.
- if (vim_strchr(part_buf, COM_MIDDLE) != NULL) {
- if (middle_match_len == 0) {
- middle_match_len = j;
- saved_flags = prev_list;
- }
- continue;
- }
- if (middle_match_len != 0 && j > middle_match_len) {
- // Use this match instead of the middle match, since it's a
- // longer thus better match.
- middle_match_len = 0;
- }
-
- if (middle_match_len == 0) {
- i += j;
- }
- found_one = TRUE;
- break;
- }
-
- if (middle_match_len != 0) {
- // Use the previously found middle match after failing to find a
- // match with an end.
- if (!got_com && flags != NULL) {
- *flags = saved_flags;
- }
- i += middle_match_len;
- found_one = TRUE;
- }
-
- // No match found, stop scanning.
- if (!found_one) {
- break;
- }
-
- result = i;
-
- // Include any trailing white space.
- while (ascii_iswhite(line[i])) {
- ++i;
- }
-
- if (include_space) {
- result = i;
- }
-
- // If this comment doesn't nest, stop here.
- got_com = TRUE;
- if (vim_strchr(part_buf, COM_NEST) == NULL) {
- break;
- }
- }
- return result;
-}
-
-/*
- * Return the offset at which the last comment in line starts. If there is no
- * comment in the whole line, -1 is returned.
- *
- * When "flags" is not null, it is set to point to the flags describing the
- * recognized comment leader.
- */
-int get_last_leader_offset(char_u *line, char_u **flags)
-{
- int result = -1;
- int i, j;
- int lower_check_bound = 0;
- char_u *string;
- char_u *com_leader;
- char_u *com_flags;
- char_u *list;
- int found_one;
- char_u part_buf[COM_MAX_LEN]; // buffer for one option part
-
- /*
- * Repeat to match several nested comment strings.
- */
- i = (int)STRLEN(line);
- while (--i >= lower_check_bound) {
- /*
- * scan through the 'comments' option for a match
- */
- found_one = FALSE;
- for (list = curbuf->b_p_com; *list;) {
- char_u *flags_save = list;
-
- /*
- * Get one option part into part_buf[]. Advance list to next one.
- * put string at start of string.
- */
- (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
- string = vim_strchr(part_buf, ':');
- if (string == NULL) { // If everything is fine, this cannot actually
- // happen.
- continue;
- }
- *string++ = NUL; // Isolate flags from string.
- com_leader = string;
-
- /*
- * Line contents and string must match.
- * When string starts with white space, must have some white space
- * (but the amount does not need to match, there might be a mix of
- * TABs and spaces).
- */
- if (ascii_iswhite(string[0])) {
- if (i == 0 || !ascii_iswhite(line[i - 1])) {
- continue;
- }
- while (ascii_iswhite(*string)) {
- string++;
- }
- }
- for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j) {
- // do nothing
- }
- if (string[j] != NUL) {
- continue;
- }
-
- /*
- * When 'b' flag used, there must be white space or an
- * end-of-line after the string in the line.
- */
- if (vim_strchr(part_buf, COM_BLANK) != NULL
- && !ascii_iswhite(line[i + j]) && line[i + j] != NUL) {
- continue;
- }
-
- if (vim_strchr(part_buf, COM_MIDDLE) != NULL) {
- // For a middlepart comment, only consider it to match if
- // everything before the current position in the line is
- // whitespace. Otherwise we would think we are inside a
- // comment if the middle part appears somewhere in the middle
- // of the line. E.g. for C the "*" appears often.
- for (j = 0; j <= i && ascii_iswhite(line[j]); j++) {
- }
- if (j < i) {
- continue;
- }
- }
-
- /*
- * We have found a match, stop searching.
- */
- found_one = TRUE;
-
- if (flags) {
- *flags = flags_save;
- }
- com_flags = flags_save;
-
- break;
- }
-
- if (found_one) {
- char_u part_buf2[COM_MAX_LEN]; // buffer for one option part
- int len1, len2, off;
-
- result = i;
- /*
- * If this comment nests, continue searching.
- */
- if (vim_strchr(part_buf, COM_NEST) != NULL) {
- continue;
- }
-
- lower_check_bound = i;
-
- // Let's verify whether the comment leader found is a substring
- // of other comment leaders. If it is, let's adjust the
- // lower_check_bound so that we make sure that we have determined
- // the comment leader correctly.
-
- while (ascii_iswhite(*com_leader)) {
- ++com_leader;
- }
- len1 = (int)STRLEN(com_leader);
-
- for (list = curbuf->b_p_com; *list;) {
- char_u *flags_save = list;
-
- (void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ",");
- if (flags_save == com_flags) {
- continue;
- }
- string = vim_strchr(part_buf2, ':');
- ++string;
- while (ascii_iswhite(*string)) {
- ++string;
- }
- len2 = (int)STRLEN(string);
- if (len2 == 0) {
- continue;
- }
-
- // Now we have to verify whether string ends with a substring
- // beginning the com_leader.
- for (off = (len2 > i ? i : len2); off > 0 && off + len1 > len2;) {
- --off;
- if (!STRNCMP(string + off, com_leader, len2 - off)) {
- if (i - off < lower_check_bound) {
- lower_check_bound = i - off;
- }
- }
- }
- }
- }
- }
- return result;
-}
-
-int gchar_pos(pos_T *pos)
- FUNC_ATTR_NONNULL_ARG(1)
-{
- // When searching columns is sometimes put at the end of a line.
- if (pos->col == MAXCOL) {
- return NUL;
- }
- return utf_ptr2char(ml_get_pos(pos));
-}
-
-/*
- * check_status: called when the status bars for the buffer 'buf'
- * need to be updated
- */
-void check_status(buf_T *buf)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf && wp->w_status_height) {
- wp->w_redr_status = TRUE;
- if (must_redraw < VALID) {
- must_redraw = VALID;
- }
- }
- }
-}
-
-/// Ask for a reply from the user, 'y' or 'n'
-///
-/// No other characters are accepted, the message is repeated until a valid
-/// reply is entered or <C-c> is hit.
-///
-/// @param[in] str Prompt: question to ask user. Is always followed by
-/// " (y/n)?".
-/// @param[in] direct Determines what function to use to get user input. If
-/// true then ui_inchar() will be used, otherwise vgetc().
-/// I.e. when direct is true then characters are obtained
-/// directly from the user without buffers involved.
-///
-/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt.
-int ask_yesno(const char *const str, const bool direct)
-{
- const int save_State = State;
-
- no_wait_return++;
- State = CONFIRM; // Mouse behaves like with :confirm.
- setmouse(); // Disable mouse in xterm.
- no_mapping++;
-
- int r = ' ';
- while (r != 'y' && r != 'n') {
- // Same highlighting as for wait_return.
- smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str);
- if (direct) {
- r = get_keystroke(NULL);
- } else {
- r = plain_vgetc();
- }
- if (r == Ctrl_C || r == ESC) {
- r = 'n';
- }
- msg_putchar(r); // Show what you typed.
- ui_flush();
- }
- no_wait_return--;
- State = save_State;
- setmouse();
- no_mapping--;
-
- return r;
-}
-
-/*
- * Return TRUE if "c" is a mouse key.
- */
-int is_mouse_key(int c)
-{
- return c == K_LEFTMOUSE
- || c == K_LEFTMOUSE_NM
- || c == K_LEFTDRAG
- || c == K_LEFTRELEASE
- || c == K_LEFTRELEASE_NM
- || c == K_MOUSEMOVE
- || c == K_MIDDLEMOUSE
- || c == K_MIDDLEDRAG
- || c == K_MIDDLERELEASE
- || c == K_RIGHTMOUSE
- || c == K_RIGHTDRAG
- || c == K_RIGHTRELEASE
- || c == K_MOUSEDOWN
- || c == K_MOUSEUP
- || c == K_MOUSELEFT
- || c == K_MOUSERIGHT
- || c == K_X1MOUSE
- || c == K_X1DRAG
- || c == K_X1RELEASE
- || c == K_X2MOUSE
- || c == K_X2DRAG
- || c == K_X2RELEASE;
-}
-
-/*
- * Get a key stroke directly from the user.
- * Ignores mouse clicks and scrollbar events, except a click for the left
- * button (used at the more prompt).
- * Doesn't use vgetc(), because it syncs undo and eats mapped characters.
- * Disadvantage: typeahead is ignored.
- * Translates the interrupt character for unix to ESC.
- */
-int get_keystroke(MultiQueue *events)
-{
- char_u *buf = NULL;
- int buflen = 150;
- int maxlen;
- int len = 0;
- int n;
- int save_mapped_ctrl_c = mapped_ctrl_c;
- int waited = 0;
-
- mapped_ctrl_c = 0; // mappings are not used here
- for (;;) {
- // flush output before waiting
- ui_flush();
- // Leave some room for check_termcode() to insert a key code into (max
- // 5 chars plus NUL). And fix_input_buffer() can triple the number of
- // bytes.
- maxlen = (buflen - 6 - len) / 3;
- if (buf == NULL) {
- buf = xmalloc((size_t)buflen);
- } else if (maxlen < 10) {
- // Need some more space. This might happen when receiving a long
- // escape sequence.
- buflen += 100;
- buf = xrealloc(buf, (size_t)buflen);
- maxlen = (buflen - 6 - len) / 3;
- }
-
- // First time: blocking wait. Second time: wait up to 100ms for a
- // terminal code to complete.
- n = os_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0, events);
- if (n > 0) {
- // Replace zero and CSI by a special key code.
- n = fix_input_buffer(buf + len, n);
- len += n;
- waited = 0;
- } else if (len > 0) {
- ++waited; // keep track of the waiting time
- }
- if (n > 0) { // found a termcode: adjust length
- len = n;
- }
- if (len == 0) { // nothing typed yet
- continue;
- }
-
- // Handle modifier and/or special key code.
- n = buf[0];
- if (n == K_SPECIAL) {
- n = TO_SPECIAL(buf[1], buf[2]);
- if (buf[1] == KS_MODIFIER
- || n == K_IGNORE
- || (is_mouse_key(n) && n != K_LEFTMOUSE)) {
- if (buf[1] == KS_MODIFIER) {
- mod_mask = buf[2];
- }
- len -= 3;
- if (len > 0) {
- memmove(buf, buf + 3, (size_t)len);
- }
- continue;
- }
- break;
- }
- if (MB_BYTE2LEN(n) > len) {
- // more bytes to get.
- continue;
- }
- buf[len >= buflen ? buflen - 1 : len] = NUL;
- n = utf_ptr2char(buf);
- break;
- }
- xfree(buf);
-
- mapped_ctrl_c = save_mapped_ctrl_c;
- return n;
-}
-
-/// Get a number from the user.
-/// When "mouse_used" is not NULL allow using the mouse.
-///
-/// @param colon allow colon to abort
-int get_number(int colon, int *mouse_used)
-{
- int n = 0;
- int c;
- int typed = 0;
-
- if (mouse_used != NULL) {
- *mouse_used = FALSE;
- }
-
- // When not printing messages, the user won't know what to type, return a
- // zero (as if CR was hit).
- if (msg_silent != 0) {
- return 0;
- }
-
- no_mapping++;
- for (;;) {
- ui_cursor_goto(msg_row, msg_col);
- c = safe_vgetc();
- if (ascii_isdigit(c)) {
- n = n * 10 + c - '0';
- msg_putchar(c);
- ++typed;
- } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) {
- if (typed > 0) {
- msg_puts("\b \b");
- --typed;
- }
- n /= 10;
- } else if (mouse_used != NULL && c == K_LEFTMOUSE) {
- *mouse_used = TRUE;
- n = mouse_row + 1;
- break;
- } else if (n == 0 && c == ':' && colon) {
- stuffcharReadbuff(':');
- if (!exmode_active) {
- cmdline_row = msg_row;
- }
- skip_redraw = true; // skip redraw once
- do_redraw = false;
- break;
- } else if (c == Ctrl_C || c == ESC || c == 'q') {
- n = 0;
- break;
- } else if (c == CAR || c == NL) {
- break;
- }
- }
- no_mapping--;
- return n;
-}
-
-/*
- * Ask the user to enter a number.
- * When "mouse_used" is not NULL allow using the mouse and in that case return
- * the line number.
- */
-int prompt_for_number(int *mouse_used)
-{
- int i;
- int save_cmdline_row;
- int save_State;
-
- // When using ":silent" assume that <CR> was entered.
- if (mouse_used != NULL) {
- msg_puts(_("Type number and <Enter> or click with the mouse "
- "(q or empty cancels): "));
- } else {
- msg_puts(_("Type number and <Enter> (q or empty cancels): "));
- }
-
- /* Set the state such that text can be selected/copied/pasted and we still
- * get mouse events. */
- save_cmdline_row = cmdline_row;
- cmdline_row = 0;
- save_State = State;
- State = ASKMORE; // prevents a screen update when using a timer
- // May show different mouse shape.
- setmouse();
-
- i = get_number(TRUE, mouse_used);
- if (KeyTyped) {
- // don't call wait_return() now
- if (msg_row > 0) {
- cmdline_row = msg_row - 1;
- }
- need_wait_return = false;
- msg_didany = false;
- msg_didout = false;
- } else {
- cmdline_row = save_cmdline_row;
- }
- State = save_State;
- // May need to restore mouse shape.
- setmouse();
-
- return i;
-}
-
-void msgmore(long n)
-{
- long pn;
-
- if (global_busy // no messages now, wait until global is finished
- || !messaging()) { // 'lazyredraw' set, don't do messages now
- return;
- }
-
- // We don't want to overwrite another important message, but do overwrite
- // a previous "more lines" or "fewer lines" message, so that "5dd" and
- // then "put" reports the last action.
- if (keep_msg != NULL && !keep_msg_more) {
- return;
- }
-
- if (n > 0) {
- pn = n;
- } else {
- pn = -n;
- }
-
- if (pn > p_report) {
- if (n > 0) {
- vim_snprintf(msg_buf, MSG_BUF_LEN,
- NGETTEXT("%ld more line", "%ld more lines", pn),
- pn);
- } else {
- vim_snprintf(msg_buf, MSG_BUF_LEN,
- NGETTEXT("%ld line less", "%ld fewer lines", pn),
- pn);
- }
- if (got_int) {
- xstrlcat(msg_buf, _(" (Interrupted)"), MSG_BUF_LEN);
- }
- if (msg(msg_buf)) {
- set_keep_msg(msg_buf, 0);
- keep_msg_more = true;
- }
- }
-}
-
-/*
- * flush map and typeahead buffers and give a warning for an error
- */
-void beep_flush(void)
-{
- if (emsg_silent == 0) {
- flush_buffers(FLUSH_MINIMAL);
- vim_beep(BO_ERROR);
- }
-}
-
-// Give a warning for an error
-// val is one of the BO_ values, e.g., BO_OPER
-void vim_beep(unsigned val)
-{
- called_vim_beep = true;
-
- if (emsg_silent == 0) {
- if (!((bo_flags & val) || (bo_flags & BO_ALL))) {
- static int beeps = 0;
- static uint64_t start_time = 0;
-
- // Only beep up to three times per half a second,
- // otherwise a sequence of beeps would freeze Vim.
- if (start_time == 0 || os_hrtime() - start_time > 500000000u) {
- beeps = 0;
- start_time = os_hrtime();
- }
- beeps++;
- if (beeps <= 3) {
- if (p_vb) {
- ui_call_visual_bell();
- } else {
- ui_call_bell();
- }
- }
- }
-
- // When 'debug' contains "beep" produce a message. If we are sourcing
- // a script or executing a function give the user a hint where the beep
- // comes from.
- if (vim_strchr(p_debug, 'e') != NULL) {
- msg_source(HL_ATTR(HLF_W));
- msg_attr(_("Beep!"), HL_ATTR(HLF_W));
- }
- }
-}
-
-#if defined(EXITFREE)
-
-void free_users(void)
-{
- ga_clear_strings(&ga_users);
-}
-
-#endif
-
-/*
- * Find all user names for user completion.
- * Done only once and then cached.
- */
-static void init_users(void)
-{
- static int lazy_init_done = FALSE;
-
- if (lazy_init_done) {
- return;
- }
-
- lazy_init_done = TRUE;
-
- os_get_usernames(&ga_users);
-}
-
-/*
- * Function given to ExpandGeneric() to obtain an user names.
- */
-char_u *get_users(expand_T *xp, int idx)
-{
- init_users();
- if (idx < ga_users.ga_len) {
- return ((char_u **)ga_users.ga_data)[idx];
- }
- return NULL;
-}
-
-/*
- * Check whether name matches a user name. Return:
- * 0 if name does not match any user name.
- * 1 if name partially matches the beginning of a user name.
- * 2 is name fully matches a user name.
- */
-int match_user(char_u *name)
-{
- int n = (int)STRLEN(name);
- int result = 0;
-
- init_users();
- for (int i = 0; i < ga_users.ga_len; i++) {
- if (STRCMP(((char_u **)ga_users.ga_data)[i], name) == 0) {
- return 2; // full match
- }
- if (STRNCMP(((char_u **)ga_users.ga_data)[i], name, n) == 0) {
- result = 1; // partial match
- }
- }
- return result;
-}
-
-/// Preserve files and exit.
-/// @note IObuff must contain a message.
-/// @note This may be called from deadly_signal() in a signal handler, avoid
-/// unsafe functions, such as allocating memory.
-void preserve_exit(void)
- FUNC_ATTR_NORETURN
-{
- // 'true' when we are sure to exit, e.g., after a deadly signal
- static bool really_exiting = false;
-
- // Prevent repeated calls into this method.
- if (really_exiting) {
- if (input_global_fd() >= 0) {
- // normalize stream (#2598)
- stream_set_blocking(input_global_fd(), true);
- }
- exit(2);
- }
-
- really_exiting = true;
- // Ignore SIGHUP while we are already exiting. #9274
- signal_reject_deadly();
- mch_errmsg(IObuff);
- mch_errmsg("\n");
- ui_flush();
-
- ml_close_notmod(); // close all not-modified buffers
-
- FOR_ALL_BUFFERS(buf) {
- if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) {
- mch_errmsg("Vim: preserving files...\r\n");
- ui_flush();
- ml_sync_all(false, false, true); // preserve all swap files
- break;
- }
- }
-
- ml_close_all(false); // close all memfiles, without deleting
-
- mch_errmsg("Vim: Finished.\r\n");
-
- getout(1);
-}
-
-/*
- * Check for CTRL-C pressed, but only once in a while.
- * Should be used instead of os_breakcheck() for functions that check for
- * each line in the file. Calling os_breakcheck() each time takes too much
- * time, because it can be a system call.
- */
-
-#ifndef BREAKCHECK_SKIP
-# define BREAKCHECK_SKIP 1000
-#endif
-
-static int breakcheck_count = 0;
-
-void line_breakcheck(void)
-{
- if (++breakcheck_count >= BREAKCHECK_SKIP) {
- breakcheck_count = 0;
- os_breakcheck();
- }
-}
-
-/*
- * Like line_breakcheck() but check 10 times less often.
- */
-void fast_breakcheck(void)
-{
- if (++breakcheck_count >= BREAKCHECK_SKIP * 10) {
- breakcheck_count = 0;
- os_breakcheck();
- }
-}
-
-// Like line_breakcheck() but check 100 times less often.
-void veryfast_breakcheck(void)
-{
- if (++breakcheck_count >= BREAKCHECK_SKIP * 100) {
- breakcheck_count = 0;
- os_breakcheck();
- }
-}
-
-/// os_call_shell() wrapper. Handles 'verbose', :profile, and v:shell_error.
-/// Invalidates cached tags.
-///
-/// @return shell command exit code
-int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
-{
- int retval;
- proftime_T wait_time;
-
- if (p_verbose > 3) {
- verbose_enter();
- smsg(_("Executing command: \"%s\""), cmd == NULL ? p_sh : cmd);
- msg_putchar('\n');
- verbose_leave();
- }
-
- if (do_profiling == PROF_YES) {
- prof_child_enter(&wait_time);
- }
-
- if (*p_sh == NUL) {
- emsg(_(e_shellempty));
- retval = -1;
- } else {
- // The external command may update a tags file, clear cached tags.
- tag_freematch();
-
- retval = os_call_shell(cmd, opts, extra_shell_arg);
- }
-
- set_vim_var_nr(VV_SHELL_ERROR, (varnumber_T)retval);
- if (do_profiling == PROF_YES) {
- prof_child_exit(&wait_time);
- }
-
- return retval;
-}
-
-/// Get the stdout of an external command.
-/// If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not
-/// NULL store the length there.
-///
-/// @param cmd command to execute
-/// @param infile optional input file name
-/// @param flags can be kShellOptSilent or 0
-/// @param ret_len length of the stdout
-///
-/// @return an allocated string, or NULL for error.
-char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, size_t *ret_len)
-{
- char_u *buffer = NULL;
-
- if (check_secure()) {
- return NULL;
- }
-
- // get a name for the temp file
- char_u *tempname = vim_tempname();
- if (tempname == NULL) {
- emsg(_(e_notmp));
- return NULL;
- }
-
- // Add the redirection stuff
- char_u *command = make_filter_cmd(cmd, infile, tempname);
-
- /*
- * Call the shell to execute the command (errors are ignored).
- * Don't check timestamps here.
- */
- ++no_check_timestamps;
- call_shell(command, kShellOptDoOut | kShellOptExpand | flags, NULL);
- --no_check_timestamps;
-
- xfree(command);
-
- // read the names from the file into memory
- FILE *fd = os_fopen((char *)tempname, READBIN);
-
- if (fd == NULL) {
- semsg(_(e_notopen), tempname);
- goto done;
- }
-
- fseek(fd, 0L, SEEK_END);
- size_t len = (size_t)ftell(fd); // get size of temp file
- fseek(fd, 0L, SEEK_SET);
-
- buffer = xmalloc(len + 1);
- size_t i = fread((char *)buffer, 1, len, fd);
- fclose(fd);
- os_remove((char *)tempname);
- if (i != len) {
- semsg(_(e_notread), tempname);
- XFREE_CLEAR(buffer);
- } else if (ret_len == NULL) {
- // Change NUL into SOH, otherwise the string is truncated.
- for (i = 0; i < len; ++i) {
- if (buffer[i] == NUL) {
- buffer[i] = 1;
- }
- }
-
- buffer[len] = NUL; // make sure the buffer is terminated
- } else {
- *ret_len = len;
- }
-
-done:
- xfree(tempname);
- return buffer;
-}
-
-/*
- * Free the list of files returned by expand_wildcards() or other expansion
- * functions.
- */
-void FreeWild(int count, char_u **files)
-{
- if (count <= 0 || files == NULL) {
- return;
- }
- while (count--) {
- xfree(files[count]);
- }
- xfree(files);
-}
-
-/*
- * Return TRUE when need to go to Insert mode because of 'insertmode'.
- * Don't do this when still processing a command or a mapping.
- * Don't do this when inside a ":normal" command.
- */
-int goto_im(void)
-{
- return p_im && stuff_empty() && typebuf_typed();
-}
-
-/// Put the timestamp of an undo header in "buf[buflen]" in a nice format.
-void add_time(char_u *buf, size_t buflen, time_t tt)
-{
- struct tm curtime;
-
- if (time(NULL) - tt >= 100) {
- os_localtime_r(&tt, &curtime);
- if (time(NULL) - tt < (60L * 60L * 12L)) {
- // within 12 hours
- (void)strftime((char *)buf, buflen, "%H:%M:%S", &curtime);
- } else {
- // longer ago
- (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", &curtime);
- }
- } else {
- int64_t seconds = time(NULL) - tt;
- vim_snprintf((char *)buf, buflen,
- NGETTEXT("%" PRId64 " second ago",
- "%" PRId64 " seconds ago", (uint32_t)seconds),
- seconds);
- }
-}
-
-dict_T *get_v_event(save_v_event_T *sve)
-{
- dict_T *v_event = get_vim_var_dict(VV_EVENT);
-
- if (v_event->dv_hashtab.ht_used > 0) {
- // recursive use of v:event, save, make empty and restore later
- sve->sve_did_save = true;
- sve->sve_hashtab = v_event->dv_hashtab;
- hash_init(&v_event->dv_hashtab);
- } else {
- sve->sve_did_save = false;
- }
- return v_event;
-}
-
-void restore_v_event(dict_T *v_event, save_v_event_T *sve)
-{
- tv_dict_free_contents(v_event);
- if (sve->sve_did_save) {
- v_event->dv_hashtab = sve->sve_hashtab;
- } else {
- hash_init(&v_event->dv_hashtab);
- }
-}
-
-/// Fires a ModeChanged autocmd.
-void trigger_modechanged(void)
-{
- if (!has_event(EVENT_MODECHANGED)) {
- return;
- }
-
- char *mode = get_mode();
- if (STRCMP(mode, last_mode) == 0) {
- xfree(mode);
- return;
- }
-
- save_v_event_T save_v_event;
- dict_T *v_event = get_v_event(&save_v_event);
- tv_dict_add_str(v_event, S_LEN("new_mode"), mode);
- tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode);
-
- char_u *pat_pre = concat_str((char_u *)last_mode, (char_u *)":");
- char_u *pat = concat_str(pat_pre, (char_u *)mode);
- xfree(pat_pre);
-
- apply_autocmds(EVENT_MODECHANGED, pat, NULL, false, curbuf);
- xfree(last_mode);
- last_mode = mode;
-
- xfree(pat);
- restore_v_event(v_event, &save_v_event);
-}
diff --git a/src/nvim/misc1.h b/src/nvim/misc1.h
deleted file mode 100644
index 4ce142c4c5..0000000000
--- a/src/nvim/misc1.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef NVIM_MISC1_H
-#define NVIM_MISC1_H
-
-#include "nvim/os/shell.h"
-#include "nvim/vim.h"
-
-// flags for open_line()
-#define OPENLINE_DELSPACES 1 // delete spaces after cursor
-#define OPENLINE_DO_COM 2 // format comments
-#define OPENLINE_KEEPTRAIL 4 // keep trailing spaces
-#define OPENLINE_MARKFIX 8 // fix mark positions
-#define OPENLINE_COM_LIST 16 // format comments with list/2nd line indent
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "misc1.h.generated.h"
-#endif
-#endif // NVIM_MISC1_H
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 386094e509..5d007fb173 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -10,7 +10,6 @@
#include "nvim/diff.h"
#include "nvim/fold.h"
#include "nvim/memline.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/os_unix.h"
@@ -31,6 +30,32 @@
static linenr_T orig_topline = 0;
static int orig_topfill = 0;
+/// Return true if "c" is a mouse key.
+bool is_mouse_key(int c)
+{
+ return c == K_LEFTMOUSE
+ || c == K_LEFTMOUSE_NM
+ || c == K_LEFTDRAG
+ || c == K_LEFTRELEASE
+ || c == K_LEFTRELEASE_NM
+ || c == K_MOUSEMOVE
+ || c == K_MIDDLEMOUSE
+ || c == K_MIDDLEDRAG
+ || c == K_MIDDLERELEASE
+ || c == K_RIGHTMOUSE
+ || c == K_RIGHTDRAG
+ || c == K_RIGHTRELEASE
+ || c == K_MOUSEDOWN
+ || c == K_MOUSEUP
+ || c == K_MOUSELEFT
+ || c == K_MOUSERIGHT
+ || c == K_X1MOUSE
+ || c == K_X1DRAG
+ || c == K_X1RELEASE
+ || c == K_X2MOUSE
+ || c == K_X2DRAG
+ || c == K_X2RELEASE;
+}
/// Move the cursor to the specified row and column on the screen.
/// Change current window if necessary. Returns an integer with the
/// CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
diff --git a/src/nvim/move.c b/src/nvim/move.c
index d80e63e79d..15ba6645f5 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -23,9 +23,9 @@
#include "nvim/diff.h"
#include "nvim/edit.h"
#include "nvim/fold.h"
+#include "nvim/getchar.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
@@ -1011,7 +1011,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
col -= wp->w_leftcol;
if (col >= 0 && col < wp->w_width) {
- coloff = col - scol + (local ? 0 : wp->w_wincol) + 1;
+ coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_border_adj[3]) + 1;
} else {
scol = ccol = ecol = 0;
// character is left or right of the window
@@ -1022,7 +1022,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
}
}
}
- *rowp = (local ? 0 : wp->w_winrow) + row + rowoff;
+ *rowp = (local ? 0 : wp->w_winrow + wp->w_border_adj[0]) + row + rowoff;
*scolp = scol + coloff;
*ccolp = ccol + coloff;
*ecolp = ecol + coloff;
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index a1a1f0f8c0..299651ee97 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -24,7 +24,6 @@
#include "nvim/map.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/os/input.h"
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 9332c55b5f..3246596f16 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -40,7 +40,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
@@ -230,7 +229,7 @@ static const struct nv_cmd {
{ 'N', nv_next, 0, SEARCH_REV },
{ 'O', nv_open, 0, 0 },
{ 'P', nv_put, 0, 0 },
- { 'Q', nv_exmode, NV_NCW, 0 },
+ { 'Q', nv_regreplay, 0, 0 },
{ 'R', nv_Replace, 0, false },
{ 'S', nv_subst, NV_KEEPREG, 0 },
{ 'T', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD },
@@ -4029,15 +4028,18 @@ dozet:
/*
* "Q" command.
*/
-static void nv_exmode(cmdarg_T *cap)
+static void nv_regreplay(cmdarg_T *cap)
{
- /*
- * Ignore 'Q' in Visual mode, just give a beep.
- */
- if (VIsual_active) {
- vim_beep(BO_EX);
- } else if (!checkclearop(cap->oap)) {
- do_exmode();
+ if (checkclearop(cap->oap)) {
+ return;
+ }
+
+ while (cap->count1-- && !got_int) {
+ if (do_execreg(reg_recorded, false, false, false) == false) {
+ clearopbeep(cap->oap);
+ break;
+ }
+ line_breakcheck();
}
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 52c382028e..c6f9c5f04f 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -38,7 +38,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
@@ -913,13 +912,14 @@ int do_record(int c)
showmode();
regname = c;
retval = OK;
- }
- } else { // stop recording
- /*
- * Get the recorded key hits. K_SPECIAL and CSI will be escaped, this
- * needs to be removed again to put it in a register. exec_reg then
- * adds the escaping back later.
- */
+ apply_autocmds(EVENT_RECORDINGENTER, NULL, NULL, false, curbuf);
+ }
+ } else { // stop recording
+ // Get the recorded key hits. K_SPECIAL and CSI will be escaped, this
+ // needs to be removed again to put it in a register. exec_reg then
+ // adds the escaping back later.
+ apply_autocmds(EVENT_RECORDINGLEAVE, NULL, NULL, false, curbuf);
+ reg_recorded = reg_recording;
reg_recording = 0;
if (ui_has(kUIMessages)) {
showmode();
@@ -933,10 +933,8 @@ int do_record(int c)
// Remove escaping for CSI and K_SPECIAL in multi-byte chars.
vim_unescape_csi(p);
- /*
- * We don't want to change the default register here, so save and
- * restore the current register name.
- */
+ // We don't want to change the default register here, so save and
+ // restore the current register name.
old_y_previous = y_previous;
retval = stuff_yank(regname, p);
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 1fe2e1d04c..65adda3c01 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -55,7 +55,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
@@ -262,6 +261,7 @@ typedef struct vimoption {
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
"i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr," \
+ "G:CursorLineSign,O:CursorLineFold" \
"r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg," \
"W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn," \
"-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar," \
@@ -2991,7 +2991,7 @@ ambw_end:
}
} else if (varp == &curwin->w_p_fdc || varp == &curwin->w_allbuf_opt.wo_fdc) {
// 'foldcolumn'
- if (check_opt_strings(*varp, p_fdc_values, false) != OK) {
+ if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) {
errmsg = e_invarg;
}
} else if (varp == &p_pt) {
@@ -3370,6 +3370,9 @@ static int int_cmp(const void *a, const void *b)
/// @return OK when the value is valid, FAIL otherwise
int check_signcolumn(char_u *val)
{
+ if (*val == NUL) {
+ return FAIL;
+ }
// check for basic match
if (check_opt_strings(val, p_scl_values, false) == OK) {
return OK;
@@ -3610,7 +3613,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
c2 = c3 = 0;
s = p + len + 1;
c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || utf_char2cells(c1) > 1) {
+ if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
@@ -3618,12 +3621,12 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
return e_invarg;
}
c2 = get_encoded_char_adv(&s);
- if (c2 == 0 || utf_char2cells(c2) > 1) {
+ if (c2 == 0 || char2cells(c2) > 1) {
return e_invarg;
}
if (!(*s == ',' || *s == NUL)) {
c3 = get_encoded_char_adv(&s);
- if (c3 == 0 || utf_char2cells(c3) > 1) {
+ if (c3 == 0 || char2cells(c3) > 1) {
return e_invarg;
}
}
@@ -3657,7 +3660,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
multispace_len = 0;
while (*s != NUL && *s != ',') {
c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || utf_char2cells(c1) > 1) {
+ if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
multispace_len++;
@@ -5059,6 +5062,9 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
/// @param[in] number New value for the number or boolean option.
/// @param[in] string New value for string option.
/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
+/// If OPT_CLEAR is set, the value of the option
+/// is cleared (the exact semantics of this depend
+/// on the option).
///
/// @return NULL on success, error message on error.
char *set_option_value(const char *const name, const long number, const char *const string,
@@ -5084,7 +5090,7 @@ char *set_option_value(const char *const name, const long number, const char *co
}
if (flags & P_STRING) {
const char *s = string;
- if (s == NULL) {
+ if (s == NULL || opt_flags & OPT_CLEAR) {
s = "";
}
return set_string_option(opt_idx, s, opt_flags);
@@ -5106,10 +5112,23 @@ char *set_option_value(const char *const name, const long number, const char *co
return NULL; // do nothing as we hit an error
}
}
+ long numval = number;
+ if (opt_flags & OPT_CLEAR) {
+ if ((int *)varp == &curbuf->b_p_ar) {
+ numval = -1;
+ } else if ((long *)varp == &curbuf->b_p_ul) {
+ numval = NO_LOCAL_UNDOLEVEL;
+ } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) {
+ numval = -1;
+ } else {
+ char *s = NULL;
+ (void)get_option_value(name, &numval, (char_u **)&s, OPT_GLOBAL);
+ }
+ }
if (flags & P_NUM) {
- return set_num_option(opt_idx, varp, number, NULL, 0, opt_flags);
+ return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags);
} else {
- return set_bool_option(opt_idx, varp, (int)number, opt_flags);
+ return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
}
}
}
@@ -7898,7 +7917,7 @@ void set_fileformat(int eol_style, int opt_flags)
}
// This may cause the buffer to become (un)modified.
- check_status(curbuf);
+ redraw_buf_status_later(curbuf);
redraw_tabline = true;
need_maketitle = true; // Set window title later.
}
diff --git a/src/nvim/option.h b/src/nvim/option.h
index 452494172f..f7dbaafeec 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -22,6 +22,7 @@ typedef enum {
OPT_ONECOLUMN = 64, ///< list options one per line
OPT_NO_REDRAW = 128, ///< ignore redraw flags on option
OPT_SKIPRTP = 256, ///< "skiprtp" in 'sessionoptions'
+ OPT_CLEAR = 512, ///< Clear local value of an option.
} OptionFlags;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 19cb33a354..09c3bf3800 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -743,6 +743,7 @@ EXTERN int p_write; // 'write'
EXTERN int p_wa; // 'writeany'
EXTERN int p_wb; // 'writebackup'
EXTERN long p_wd; // 'writedelay'
+EXTERN int p_cdh; // 'cdhome'
EXTERN int p_force_on; ///< options that cannot be turned off.
EXTERN int p_force_off; ///< options that cannot be turned on.
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 71208dfc68..28b4eb9fe2 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -275,6 +275,14 @@ return {
defaults={if_true="internal,keepascii"}
},
{
+ full_name='cdhome', abbreviation='cdh',
+ short_desc=N_(":cd without argument goes to the home directory"),
+ type='bool', scope={'global'},
+ secure=true,
+ varname='p_cdh',
+ defaults={if_true=false}
+ },
+ {
full_name='cdpath', abbreviation='cd',
short_desc=N_("list of directories searched with \":cd\""),
type='string', list='comma', scope={'global'},
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index e9963516fc..24c7678633 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -21,7 +21,6 @@
#include "nvim/assert.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 5b231f205b..3790eba212 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -17,7 +17,6 @@
#include "nvim/main.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
-#include "nvim/misc1.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/input.h"
#include "nvim/state.h"
@@ -183,6 +182,40 @@ void os_breakcheck(void)
updating_screen = save_us;
}
+#define BREAKCHECK_SKIP 1000
+static int breakcheck_count = 0;
+
+/// Check for CTRL-C pressed, but only once in a while.
+///
+/// Should be used instead of os_breakcheck() for functions that check for
+/// each line in the file. Calling os_breakcheck() each time takes too much
+/// time, because it will use system calls to check for input.
+void line_breakcheck(void)
+{
+ if (++breakcheck_count >= BREAKCHECK_SKIP) {
+ breakcheck_count = 0;
+ os_breakcheck();
+ }
+}
+
+/// Like line_breakcheck() but check 10 times less often.
+void fast_breakcheck(void)
+{
+ if (++breakcheck_count >= BREAKCHECK_SKIP * 10) {
+ breakcheck_count = 0;
+ os_breakcheck();
+ }
+}
+
+/// Like line_breakcheck() but check 100 times less often.
+void veryfast_breakcheck(void)
+{
+ if (++breakcheck_count >= BREAKCHECK_SKIP * 100) {
+ breakcheck_count = 0;
+ os_breakcheck();
+ }
+}
+
/// Test whether a file descriptor refers to a terminal.
///
diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h
index 8049b3b80e..dce4b0c187 100644
--- a/src/nvim/os/os_defs.h
+++ b/src/nvim/os/os_defs.h
@@ -13,6 +13,10 @@
# include "nvim/os/unix_defs.h"
#endif
+#if !defined(NAME_MAX) && defined(_XOPEN_NAME_MAX)
+#define NAME_MAX _XOPEN_NAME_MAX
+#endif
+
#define BASENAMELEN (NAME_MAX - 5)
// Use the system path length if it makes sense.
diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c
index 24ecf5c24f..3459646bad 100644
--- a/src/nvim/os/pty_process_unix.c
+++ b/src/nvim/os/pty_process_unix.c
@@ -15,6 +15,12 @@
# include <libutil.h>
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
# include <util.h>
+#elif defined(__sun)
+# include <sys/stream.h>
+# include <sys/syscall.h>
+# include <fcntl.h>
+# include <unistd.h>
+# include <signal.h>
#else
# include <pty.h>
#endif
@@ -38,6 +44,118 @@
# include "os/pty_process_unix.c.generated.h"
#endif
+#if defined(__sun) && !defined(HAVE_FORKPTY)
+
+// this header defines STR, just as nvim.h, but it is defined as ('S'<<8),
+// to avoid #undef STR, #undef STR, #define STR ('S'<<8) just delay the
+// inclusion of the header even though it gets include out of order.
+#include <sys/stropts.h>
+
+static int openpty(int *amaster, int *aslave, char *name,
+ struct termios *termp, struct winsize *winp)
+{
+ int slave = -1;
+ int master = open("/dev/ptmx", O_RDWR);
+ if (master == -1) {
+ goto error;
+ }
+
+ // grantpt will invoke a setuid program to change permissions
+ // and might fail if SIGCHLD handler is set, temporarily reset
+ // while running
+ void(*sig_saved)(int) = signal(SIGCHLD, SIG_DFL);
+ int res = grantpt(master);
+ signal(SIGCHLD, sig_saved);
+
+ if (res == -1 || unlockpt(master) == -1) {
+ goto error;
+ }
+
+ char *slave_name = ptsname(master);
+ if (slave_name == NULL) {
+ goto error;
+ }
+
+ slave = open(slave_name, O_RDWR|O_NOCTTY);
+ if (slave == -1) {
+ goto error;
+ }
+
+ // ptem emulates a terminal when used on a pseudo terminal driver,
+ // must be pushed before ldterm
+ ioctl(slave, I_PUSH, "ptem");
+ // ldterm provides most of the termio terminal interface
+ ioctl(slave, I_PUSH, "ldterm");
+ // ttcompat compatability with older terminal ioctls
+ ioctl(slave, I_PUSH, "ttcompat");
+
+ if (termp) {
+ tcsetattr(slave, TCSAFLUSH, termp);
+ }
+ if (winp) {
+ ioctl(slave, TIOCSWINSZ, winp);
+ }
+
+ *amaster = master;
+ *aslave = slave;
+ // ignoring name, not passed and size is unknown in the API
+
+ return 0;
+
+error:
+ if (slave != -1) {
+ close(slave);
+ }
+ if (master != -1) {
+ close(master);
+ }
+ return -1;
+}
+
+static int login_tty(int fd)
+{
+ setsid();
+ if (ioctl(fd, TIOCSCTTY, NULL) == -1) {
+ return -1;
+ }
+
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO) {
+ close(fd);
+ }
+
+ return 0;
+}
+
+static pid_t forkpty(int *amaster, char *name,
+ struct termios *termp, struct winsize *winp)
+{
+ int master, slave;
+ if (openpty(&master, &slave, name, termp, winp) == -1) {
+ return -1;
+ }
+
+ pid_t pid = fork();
+ switch (pid) {
+ case -1:
+ close(master);
+ close(slave);
+ return -1;
+ case 0:
+ close(master);
+ login_tty(slave);
+ return 0;
+ default:
+ close(slave);
+ *amaster = master;
+ return pid;
+ }
+}
+
+#endif
+
/// termios saved at startup (for TUI) or initialized by pty_process_spawn().
static struct termios termios_default;
@@ -198,7 +316,9 @@ static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL
termios->c_cflag = CS8|CREAD;
termios->c_lflag = ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK;
- cfsetspeed(termios, 38400);
+ // not using cfsetspeed, not available on all platforms
+ cfsetispeed(termios, 38400);
+ cfsetospeed(termios, 38400);
#ifdef IUTF8
termios->c_iflag |= IUTF8;
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 6ef0aa1091..e618b2788b 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -12,6 +12,7 @@
#include "nvim/event/libuv_process.h"
#include "nvim/event/loop.h"
#include "nvim/event/rstream.h"
+#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/fileio.h"
#include "nvim/lib/kvec.h"
@@ -20,7 +21,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/option_defs.h"
#include "nvim/os/shell.h"
#include "nvim/os/signal.h"
@@ -28,6 +28,7 @@
#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/types.h"
+#include "nvim/tag.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -681,6 +682,116 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
return exitcode;
}
+/// os_call_shell() wrapper. Handles 'verbose', :profile, and v:shell_error.
+/// Invalidates cached tags.
+///
+/// @return shell command exit code
+int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
+{
+ int retval;
+ proftime_T wait_time;
+
+ if (p_verbose > 3) {
+ verbose_enter();
+ smsg(_("Executing command: \"%s\""), cmd == NULL ? p_sh : cmd);
+ msg_putchar('\n');
+ verbose_leave();
+ }
+
+ if (do_profiling == PROF_YES) {
+ prof_child_enter(&wait_time);
+ }
+
+ if (*p_sh == NUL) {
+ emsg(_(e_shellempty));
+ retval = -1;
+ } else {
+ // The external command may update a tags file, clear cached tags.
+ tag_freematch();
+
+ retval = os_call_shell(cmd, opts, extra_shell_arg);
+ }
+
+ set_vim_var_nr(VV_SHELL_ERROR, (varnumber_T)retval);
+ if (do_profiling == PROF_YES) {
+ prof_child_exit(&wait_time);
+ }
+
+ return retval;
+}
+
+/// Get the stdout of an external command.
+/// If "ret_len" is NULL replace NUL characters with NL. When "ret_len" is not
+/// NULL store the length there.
+///
+/// @param cmd command to execute
+/// @param infile optional input file name
+/// @param flags can be kShellOptSilent or 0
+/// @param ret_len length of the stdout
+///
+/// @return an allocated string, or NULL for error.
+char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, size_t *ret_len)
+{
+ char_u *buffer = NULL;
+
+ if (check_secure()) {
+ return NULL;
+ }
+
+ // get a name for the temp file
+ char_u *tempname = vim_tempname();
+ if (tempname == NULL) {
+ emsg(_(e_notmp));
+ return NULL;
+ }
+
+ // Add the redirection stuff
+ char_u *command = make_filter_cmd(cmd, infile, tempname);
+
+ // Call the shell to execute the command (errors are ignored).
+ // Don't check timestamps here.
+ no_check_timestamps++;
+ call_shell(command, kShellOptDoOut | kShellOptExpand | flags, NULL);
+ no_check_timestamps--;
+
+ xfree(command);
+
+ // read the names from the file into memory
+ FILE *fd = os_fopen((char *)tempname, READBIN);
+
+ if (fd == NULL) {
+ semsg(_(e_notopen), tempname);
+ goto done;
+ }
+
+ fseek(fd, 0L, SEEK_END);
+ size_t len = (size_t)ftell(fd); // get size of temp file
+ fseek(fd, 0L, SEEK_SET);
+
+ buffer = xmalloc(len + 1);
+ size_t i = fread((char *)buffer, 1, len, fd);
+ fclose(fd);
+ os_remove((char *)tempname);
+ if (i != len) {
+ semsg(_(e_notread), tempname);
+ XFREE_CLEAR(buffer);
+ } else if (ret_len == NULL) {
+ // Change NUL into SOH, otherwise the string is truncated.
+ for (i = 0; i < len; i++) {
+ if (buffer[i] == NUL) {
+ buffer[i] = 1;
+ }
+ }
+
+ buffer[len] = NUL; // make sure the buffer is terminated
+ } else {
+ *ret_len = len;
+ }
+
+done:
+ xfree(tempname);
+ return buffer;
+}
/// os_system - synchronously execute a command in the shell
///
/// example:
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index 0d125ec964..a8bf68a1a2 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -18,7 +18,6 @@
#include "nvim/main.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/misc1.h"
#include "nvim/os/signal.h"
#include "nvim/vim.h"
diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c
index 9952e2b387..e0ce3fec31 100644
--- a/src/nvim/os/users.c
+++ b/src/nvim/os/users.c
@@ -18,6 +18,9 @@
# include <lm.h>
#endif
+// All user names (for ~user completion as done by shell).
+static garray_T ga_users = GA_EMPTY_INIT_VALUE;
+
// Add a user name to the list of users in garray_T *users.
// Do nothing if user name is NULL or empty.
static void add_user(garray_T *users, char *user, bool need_copy)
@@ -157,3 +160,60 @@ char *os_get_user_directory(const char *name)
return NULL;
}
+
+#if defined(EXITFREE)
+
+void free_users(void)
+{
+ ga_clear_strings(&ga_users);
+}
+
+#endif
+
+/// Find all user names for user completion.
+///
+/// Done only once and then cached.
+static void init_users(void)
+{
+ static int lazy_init_done = false;
+
+ if (lazy_init_done) {
+ return;
+ }
+
+ lazy_init_done = true;
+
+ os_get_usernames(&ga_users);
+}
+
+/// Given to ExpandGeneric() to obtain an user names.
+char_u *get_users(expand_T *xp, int idx)
+{
+ init_users();
+ if (idx < ga_users.ga_len) {
+ return ((char_u **)ga_users.ga_data)[idx];
+ }
+ return NULL;
+}
+
+/// Check whether name matches a user name.
+///
+/// @return 0 if name does not match any user name.
+/// 1 if name partially matches the beginning of a user name.
+/// 2 is name fully matches a user name.
+int match_user(char_u *name)
+{
+ int n = (int)STRLEN(name);
+ int result = 0;
+
+ init_users();
+ for (int i = 0; i < ga_users.ga_len; i++) {
+ if (STRCMP(((char_u **)ga_users.ga_data)[i], name) == 0) {
+ return 2; // full match
+ }
+ if (STRNCMP(((char_u **)ga_users.ga_data)[i], name, n) == 0) {
+ result = 1; // partial match
+ }
+ }
+ return result;
+}
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index 9396a5896a..1398dba0e4 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -20,7 +20,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/os/input.h"
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 7b9081eafa..674d67e21a 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -18,7 +18,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
@@ -1337,6 +1336,17 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***fil
return ((flags & EW_EMPTYOK) || ga.ga_data != NULL) ? OK : FAIL;
}
+/// Free the list of files returned by expand_wildcards() or other expansion functions.
+void FreeWild(int count, char_u **files)
+{
+ if (count <= 0 || files == NULL) {
+ return;
+ }
+ while (count--) {
+ xfree(files[count]);
+ }
+ xfree(files);
+}
/*
* Return TRUE if we can expand this backtick thing here.
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 606c03f838..da2ada791f 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -386,7 +386,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
void pum_redraw(void)
{
int row = 0;
- int col;
+ int grid_col;
int attr_norm = win_hl_attr(curwin, HLF_PNI);
int attr_select = win_hl_attr(curwin, HLF_PSI);
int attr_scroll = win_hl_attr(curwin, HLF_PSB);
@@ -479,7 +479,7 @@ void pum_redraw(void)
// Display each entry, use two spaces for a Tab.
// Do this 3 times: For the main text, kind and extra info
- col = col_off;
+ grid_col = col_off;
totwidth = 0;
for (round = 1; round <= 3; ++round) {
@@ -537,24 +537,15 @@ void pum_redraw(void)
}
}
grid_puts_len(&pum_grid, rt, (int)STRLEN(rt), row,
- col - size + 1, attr);
+ grid_col - size + 1, attr);
xfree(rt_start);
xfree(st);
- col -= width;
+ grid_col -= width;
} else {
- int size = (int)STRLEN(st);
- int cells = (int)mb_string2cells(st);
-
- // only draw the text that fits
- while (size > 0 && col + cells > pum_width + pum_col) {
- size--;
- size -= utf_head_off(st, st + size);
- cells -= utf_ptr2cells(st + size);
- }
-
- grid_puts_len(&pum_grid, st, size, row, col, attr);
+ // use grid_puts_len() to truncate the text
+ grid_puts(&pum_grid, st, row, grid_col, attr);
xfree(st);
- col += width;
+ grid_col += width;
}
if (*p != TAB) {
@@ -563,12 +554,12 @@ void pum_redraw(void)
// Display two spaces for a Tab.
if (pum_rl) {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col - 1,
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col - 1,
attr);
- col -= 2;
+ grid_col -= 2;
} else {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col, attr);
- col += 2;
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col, attr);
+ grid_col += 2;
}
totwidth += 2;
// start text at next char
@@ -599,21 +590,21 @@ void pum_redraw(void)
if (pum_rl) {
grid_fill(&pum_grid, row, row + 1, col_off - pum_base_width - n + 1,
- col + 1, ' ', ' ', attr);
- col = col_off - pum_base_width - n + 1;
+ grid_col + 1, ' ', ' ', attr);
+ grid_col = col_off - pum_base_width - n + 1;
} else {
- grid_fill(&pum_grid, row, row + 1, col,
+ grid_fill(&pum_grid, row, row + 1, grid_col,
col_off + pum_base_width + n, ' ', ' ', attr);
- col = col_off + pum_base_width + n;
+ grid_col = col_off + pum_base_width + n;
}
totwidth = pum_base_width + n;
}
if (pum_rl) {
- grid_fill(&pum_grid, row, row + 1, col_off - pum_width + 1, col + 1,
+ grid_fill(&pum_grid, row, row + 1, col_off - pum_width + 1, grid_col + 1,
' ', ' ', attr);
} else {
- grid_fill(&pum_grid, row, row + 1, col, col_off + pum_width, ' ', ' ',
+ grid_fill(&pum_grid, row, row + 1, grid_col, col_off + pum_width, ' ', ' ',
attr);
}
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 6a192d148f..32d0ebe8eb 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -27,7 +27,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
@@ -658,7 +657,8 @@ static int qf_get_next_str_line(qfstate_T *state)
state->linebuf = IObuff;
state->linelen = len;
}
- STRLCPY(state->linebuf, p_str, state->linelen + 1);
+ memcpy(state->linebuf, p_str, state->linelen);
+ state->linebuf[state->linelen] = '\0';
// Increment using len in order to discard the rest of the line if it
// exceeds LINE_MAXLEN.
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index e67efb8ea0..45e580dbee 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -64,7 +64,7 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
+#include "nvim/os/input.h"
#include "nvim/plines.h"
#include "nvim/garray.h"
#include "nvim/strings.h"
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 5df5cc5975..41c927eaa6 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -16,23 +16,22 @@
#include "nvim/ascii.h"
#include "nvim/garray.h"
-
-/*
- * Logging of NFA engine.
- *
- * The NFA engine can write four log files:
- * - Error log: Contains NFA engine's fatal errors.
- * - Dump log: Contains compiled NFA state machine's information.
- * - Run log: Contains information of matching procedure.
- * - Debug log: Contains detailed information of matching procedure. Can be
- * disabled by undefining NFA_REGEXP_DEBUG_LOG.
- * The first one can also be used without debug mode.
- * The last three are enabled when compiled as debug mode and individually
- * disabled by commenting them out.
- * The log files can get quite big!
- * Do disable all of this when compiling Vim for debugging, undefine REGEXP_DEBUG in
- * regexp.c
- */
+#include "nvim/os/input.h"
+
+// Logging of NFA engine.
+//
+// The NFA engine can write four log files:
+// - Error log: Contains NFA engine's fatal errors.
+// - Dump log: Contains compiled NFA state machine's information.
+// - Run log: Contains information of matching procedure.
+// - Debug log: Contains detailed information of matching procedure. Can be
+// disabled by undefining NFA_REGEXP_DEBUG_LOG.
+// The first one can also be used without debug mode.
+// The last three are enabled when compiled as debug mode and individually
+// disabled by commenting them out.
+// The log files can get quite big!
+// To disable all of this when compiling Vim for debugging, undefine REGEXP_DEBUG in
+// regexp.c
#ifdef REGEXP_DEBUG
# define NFA_REGEXP_ERROR_LOG "nfa_regexp_error.log"
# define NFA_REGEXP_DUMP_LOG "nfa_regexp_dump.log"
@@ -2566,20 +2565,20 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
ga_concat(indent, (char_u *)"| ");
else
ga_concat(indent, (char_u *)" ");
- ga_append(indent, '\0');
+ ga_append(indent, NUL);
nfa_print_state2(debugf, state->out, indent);
/* replace last part of indent for state->out1 */
indent->ga_len -= 3;
ga_concat(indent, (char_u *)" ");
- ga_append(indent, '\0');
+ ga_append(indent, NUL);
nfa_print_state2(debugf, state->out1, indent);
/* shrink indent */
indent->ga_len -= 3;
- ga_append(indent, '\0');
+ ga_append(indent, NUL);
}
/*
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index be365d4ab8..1c04cb16b3 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -12,7 +12,6 @@
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
#include "nvim/lua/executor.h"
-#include "nvim/misc1.h"
#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/runtime.h"
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index a666b9c8b0..e62e3ca7bc 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -98,7 +98,6 @@
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
@@ -314,6 +313,19 @@ void update_curbuf(int type)
update_screen(type);
}
+/// called when the status bars for the buffer 'buf' need to be updated
+void redraw_buf_status_later(buf_T *buf)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf && wp->w_status_height) {
+ wp->w_redr_status = true;
+ if (must_redraw < VALID) {
+ must_redraw = VALID;
+ }
+ }
+ }
+}
+
/// Redraw the parts of the screen that is marked for redraw.
///
/// Most code shouldn't call this directly, rather use redraw_later() and
@@ -2746,7 +2758,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
p_extra = p_extra_free;
c_extra = NUL;
c_final = NUL;
- char_attr = win_hl_attr(wp, HLF_FC);
+ if (use_cursor_line_sign(wp, lnum)) {
+ char_attr = win_hl_attr(wp, HLF_CLF);
+ } else {
+ char_attr = win_hl_attr(wp, HLF_FC);
+ }
}
}
@@ -2757,7 +2773,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
* buffer or when using Netbeans. */
int count = win_signcol_count(wp);
if (count > 0) {
- get_sign_display_info(false, wp, sattrs, row,
+ get_sign_display_info(false, wp, lnum, sattrs, row,
startrow, filler_lines, filler_todo, count,
&c_extra, &c_final, extra, sizeof(extra),
&p_extra, &n_extra,
@@ -2778,7 +2794,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'
&& num_signs > 0) {
int count = win_signcol_count(wp);
- get_sign_display_info(true, wp, sattrs, row,
+ get_sign_display_info(true, wp, lnum, sattrs, row,
startrow, filler_lines, filler_todo, count,
&c_extra, &c_final, extra, sizeof(extra),
&p_extra, &n_extra,
@@ -4617,6 +4633,14 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
}
}
+// Return true if CursorLineSign highlight is to be used.
+static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
+{
+ return wp->w_p_cul
+ && lnum == wp->w_cursor.lnum
+ && (wp->w_p_culopt_flags & CULOPT_NBR);
+}
+
// Get information needed to display the sign in line 'lnum' in window 'wp'.
// If 'nrcol' is TRUE, the sign is going to be displayed in the number column.
// Otherwise the sign is going to be displayed in the sign column.
@@ -4624,11 +4648,11 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
// @param count max number of signs
// @param[out] n_extrap number of characters from pp_extra to display
// @param[in, out] sign_idxp Index of the displayed sign
-static void get_sign_display_info(bool nrcol, win_T *wp, sign_attrs_T sattrs[], int row,
- int startrow, int filler_lines, int filler_todo, int count,
- int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size,
- char_u **pp_extra, int *n_extrap, int *char_attrp,
- int *draw_statep, int *sign_idxp)
+static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_attrs_T sattrs[],
+ int row, int startrow, int filler_lines, int filler_todo,
+ int count, int *c_extrap, int *c_finalp, char_u *extra,
+ size_t extra_size, char_u **pp_extra, int *n_extrap,
+ int *char_attrp, int *draw_statep, int *sign_idxp)
{
// Draw cells with the sign value or blank.
*c_extrap = ' ';
@@ -4636,7 +4660,11 @@ static void get_sign_display_info(bool nrcol, win_T *wp, sign_attrs_T sattrs[],
if (nrcol) {
*n_extrap = number_width(wp) + 1;
} else {
- *char_attrp = win_hl_attr(wp, HLF_SC);
+ if (use_cursor_line_sign(wp, lnum)) {
+ *char_attrp = win_hl_attr(wp, HLF_CLS);
+ } else {
+ *char_attrp = win_hl_attr(wp, HLF_SC);
+ }
*n_extrap = win_signcol_width(wp);
}
@@ -4676,7 +4704,12 @@ static void get_sign_display_info(bool nrcol, win_T *wp, sign_attrs_T sattrs[],
(*pp_extra)[*n_extrap] = NUL;
}
}
- *char_attrp = sattr->sat_texthl;
+
+ if (use_cursor_line_sign(wp, lnum) && sattr->sat_culhl > 0) {
+ *char_attrp = sattr->sat_culhl;
+ } else {
+ *char_attrp = sattr->sat_texthl;
+ }
}
}
@@ -5900,6 +5933,8 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
// Only 1 cell left, but character requires 2 cells:
// display a '>' in the last column to avoid wrapping. */
c = '>';
+ u8c = '>';
+ u8cc[0] = 0;
mbyte_cells = 1;
}
@@ -5930,6 +5965,13 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
clear_next_cell = true;
}
+ // When at the start of the text and overwriting the right half of a
+ // two-cell character in the same grid, truncate that into a '>'.
+ if (ptr == text && col > 0 && grid->chars[off][0] == 0) {
+ grid->chars[off - 1][0] = '>';
+ grid->chars[off - 1][1] = 0;
+ }
+
schar_copy(grid->chars[off], buf);
grid->attrs[off] = attr;
if (mbyte_cells == 2) {
diff --git a/src/nvim/search.c b/src/nvim/search.c
index f47315705c..906c9a6f47 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -14,6 +14,7 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/change.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
@@ -31,11 +32,11 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
+#include "nvim/os/input.h"
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/regexp.h"
@@ -1353,6 +1354,10 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
}
retval = 1; // pattern found
+ if (sia && sia->sa_wrapped) {
+ apply_autocmds(EVENT_SEARCHWRAPPED, NULL, NULL, false, NULL);
+ }
+
/*
* Add character and/or line offset
*/
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index fca73dc9f2..a308df07d1 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -31,6 +31,7 @@ struct sign {
char_u *sn_text; // text used instead of pixmap
int sn_line_hl; // highlight ID for line
int sn_text_hl; // highlight ID for text
+ int sn_cul_hl; // highlight ID for text on current line when 'cursorline' is set
int sn_num_hl; // highlight ID for line number
};
@@ -499,6 +500,9 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
if (sp->sn_line_hl != 0) {
sattr.sat_linehl = syn_id2attr(sp->sn_line_hl);
}
+ if (sp->sn_cul_hl != 0) {
+ sattr.sat_culhl = syn_id2attr(sp->sn_cul_hl);
+ }
if (sp->sn_num_hl != 0) {
sattr.sat_numhl = syn_id2attr(sp->sn_num_hl);
}
@@ -901,7 +905,7 @@ static int sign_define_init_text(sign_T *sp, char_u *text)
/// Define a new sign or update an existing sign
int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text, char_u *texthl,
- char *numhl)
+ char_u *culhl, char *numhl)
{
sign_T *sp_prev;
sign_T *sp;
@@ -939,15 +943,35 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text
}
if (linehl != NULL) {
- sp->sn_line_hl = syn_check_group((char *)linehl, (int)STRLEN(linehl));
+ if (*linehl == NUL) {
+ sp->sn_line_hl = 0;
+ } else {
+ sp->sn_line_hl = syn_check_group((char *)linehl, (int)STRLEN(linehl));
+ }
}
if (texthl != NULL) {
- sp->sn_text_hl = syn_check_group((char *)texthl, (int)STRLEN(texthl));
+ if (*texthl == NUL) {
+ sp->sn_text_hl = 0;
+ } else {
+ sp->sn_text_hl = syn_check_group((char *)texthl, (int)STRLEN(texthl));
+ }
+ }
+
+ if (culhl != NULL) {
+ if (*culhl == NUL) {
+ sp->sn_cul_hl = 0;
+ } else {
+ sp->sn_cul_hl = syn_check_group((char *)culhl, (int)STRLEN(culhl));
+ }
}
if (numhl != NULL) {
- sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl));
+ if (*numhl == NUL) {
+ sp->sn_num_hl = 0;
+ } else {
+ sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl));
+ }
}
return OK;
@@ -1133,6 +1157,7 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline)
char_u *text = NULL;
char_u *linehl = NULL;
char_u *texthl = NULL;
+ char_u *culhl = NULL;
char_u *numhl = NULL;
int failed = false;
@@ -1155,6 +1180,9 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline)
} else if (STRNCMP(arg, "texthl=", 7) == 0) {
arg += 7;
texthl = vim_strnsave(arg, (size_t)(p - arg));
+ } else if (STRNCMP(arg, "culhl=", 6) == 0) {
+ arg += 6;
+ culhl = vim_strnsave(arg, (size_t)(p - arg));
} else if (STRNCMP(arg, "numhl=", 6) == 0) {
arg += 6;
numhl = vim_strnsave(arg, (size_t)(p - arg));
@@ -1166,13 +1194,14 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline)
}
if (!failed) {
- sign_define_by_name(sign_name, icon, linehl, text, texthl, (char *)numhl);
+ sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl, (char *)numhl);
}
xfree(icon);
xfree(text);
xfree(linehl);
xfree(texthl);
+ xfree(culhl);
xfree(numhl);
}
@@ -1481,6 +1510,13 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict)
}
tv_dict_add_str(retdict, S_LEN("texthl"), (char *)p);
}
+ if (sp->sn_cul_hl > 0) {
+ p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, false);
+ if (p == NULL) {
+ p = "NONE";
+ }
+ tv_dict_add_str(retdict, S_LEN("culhl"), (char *)p);
+ }
if (sp->sn_num_hl > 0) {
p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, false);
if (p == NULL) {
@@ -1609,6 +1645,16 @@ static void sign_list_defined(sign_T *sp)
msg_puts(p);
}
}
+ if (sp->sn_cul_hl > 0) {
+ msg_puts(" culhl=");
+ const char *const p = get_highlight_name_ext(NULL,
+ sp->sn_cul_hl - 1, false);
+ if (p == NULL) {
+ msg_puts("NONE");
+ } else {
+ msg_puts(p);
+ }
+ }
if (sp->sn_num_hl > 0) {
msg_puts(" numhl=");
const char *const p = get_highlight_name_ext(NULL,
@@ -1694,7 +1740,7 @@ char_u *get_sign_name(expand_T *xp, int idx)
case EXP_SUBCMD:
return (char_u *)cmds[idx];
case EXP_DEFINE: {
- char *define_arg[] = { "icon=", "linehl=", "text=", "texthl=", "numhl=",
+ char *define_arg[] = { "culhl=", "icon=", "linehl=", "numhl=", "text=", "texthl=",
NULL };
return (char_u *)define_arg[idx];
}
@@ -1803,6 +1849,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
case SIGNCMD_DEFINE:
if (STRNCMP(last, "texthl", 6) == 0
|| STRNCMP(last, "linehl", 6) == 0
+ || STRNCMP(last, "culhl", 5) == 0
|| STRNCMP(last, "numhl", 5) == 0) {
xp->xp_context = EXPAND_HIGHLIGHT;
} else if (STRNCMP(last, "icon", 4) == 0) {
@@ -1847,6 +1894,7 @@ int sign_define_from_dict(const char *name_arg, dict_T *dict)
char *linehl = NULL;
char *text = NULL;
char *texthl = NULL;
+ char *culhl = NULL;
char *numhl = NULL;
int retval = -1;
@@ -1866,11 +1914,12 @@ int sign_define_from_dict(const char *name_arg, dict_T *dict)
linehl = tv_dict_get_string(dict, "linehl", true);
text = tv_dict_get_string(dict, "text", true);
texthl = tv_dict_get_string(dict, "texthl", true);
+ culhl = tv_dict_get_string(dict, "culhl", true);
numhl = tv_dict_get_string(dict, "numhl", true);
}
if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl,
- (char_u *)text, (char_u *)texthl, numhl)
+ (char_u *)text, (char_u *)texthl, (char_u *)culhl, numhl)
== OK) {
retval = 0;
}
@@ -1881,6 +1930,7 @@ cleanup:
xfree(linehl);
xfree(text);
xfree(texthl);
+ xfree(culhl);
xfree(numhl);
return retval;
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index 46436b2c8e..c734502878 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -38,6 +38,7 @@ typedef struct sign_attrs_S {
char_u *sat_text;
int sat_texthl;
int sat_linehl;
+ int sat_culhl;
int sat_numhl;
} sign_attrs_T;
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 85d1e139bf..bd31e98faa 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -94,12 +94,12 @@
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/hashtab.h"
+#include "nvim/input.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index ae4514dd30..42bb3c61a5 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -237,8 +237,8 @@
#include "nvim/fileio.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/misc1.h"
#include "nvim/option.h"
+#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/regexp.h"
@@ -4446,10 +4446,10 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
putc(SN_PREFCOND, fd); // <sectionID>
putc(SNF_REQUIRED, fd); // <sectionflags>
- size_t l = (size_t)write_spell_prefcond(NULL, &spin->si_prefcond);
+ size_t l = (size_t)write_spell_prefcond(NULL, &spin->si_prefcond, &fwv);
put_bytes(fd, l, 4); // <sectionlen>
- write_spell_prefcond(fd, &spin->si_prefcond);
+ write_spell_prefcond(fd, &spin->si_prefcond, &fwv);
}
// SN_REP: <repcount> <rep> ...
@@ -5793,7 +5793,7 @@ static int set_spell_finish(spelltab_T *new_st)
// Write the table with prefix conditions to the .spl file.
// When "fd" is NULL only count the length of what is written.
-static int write_spell_prefcond(FILE *fd, garray_T *gap)
+static int write_spell_prefcond(FILE *fd, garray_T *gap, size_t *fwv)
{
assert(gap->ga_len >= 0);
@@ -5801,8 +5801,7 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap)
put_bytes(fd, (uintmax_t)gap->ga_len, 2); // <prefcondcnt>
}
size_t totlen = 2 + (size_t)gap->ga_len; // <prefcondcnt> and <condlen> bytes
- size_t x = 1; // collect return value of fwrite()
- for (int i = 0; i < gap->ga_len; ++i) {
+ for (int i = 0; i < gap->ga_len; i++) {
// <prefcond> : <condlen> <condstr>
char_u *p = ((char_u **)gap->ga_data)[i];
if (p != NULL) {
@@ -5810,7 +5809,7 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap)
if (fd != NULL) {
assert(len <= INT_MAX);
fputc((int)len, fd);
- x &= fwrite(p, len, 1, fd);
+ *fwv &= fwrite(p, len, 1, fd);
}
totlen += len;
} else if (fd != NULL) {
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 71db25664f..68bc76660d 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -4,7 +4,9 @@
#include <assert.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/edit.h"
+#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/getchar.h"
#include "nvim/lib/kvec.h"
@@ -202,3 +204,33 @@ char *get_mode(void)
return buf;
}
+
+/// Fires a ModeChanged autocmd.
+void trigger_modechanged(void)
+{
+ if (!has_event(EVENT_MODECHANGED)) {
+ return;
+ }
+
+ char *mode = get_mode();
+ if (STRCMP(mode, last_mode) == 0) {
+ xfree(mode);
+ return;
+ }
+
+ save_v_event_T save_v_event;
+ dict_T *v_event = get_v_event(&save_v_event);
+ tv_dict_add_str(v_event, S_LEN("new_mode"), mode);
+ tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode);
+
+ char_u *pat_pre = concat_str((char_u *)last_mode, (char_u *)":");
+ char_u *pat = concat_str(pat_pre, (char_u *)mode);
+ xfree(pat_pre);
+
+ apply_autocmds(EVENT_MODECHANGED, pat, NULL, false, curbuf);
+ xfree(last_mode);
+ last_mode = mode;
+
+ xfree(pat);
+ restore_v_event(v_event, &save_v_event);
+}
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index c58e052ae9..e2a8108c45 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -31,7 +31,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/ops.h"
#include "nvim/option.h"
@@ -394,6 +393,18 @@ void del_trailing_spaces(char_u *ptr)
}
}
+#if !defined(HAVE_STRNLEN)
+size_t xstrnlen(const char *s, size_t n)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
+{
+ const char *end = memchr(s, '\0', n);
+ if (end == NULL) {
+ return n;
+ }
+ return end - s;
+}
+#endif
+
#if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP))
/*
* Compare two strings, ignoring case, using current locale.
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index cb243668ce..a9447165c2 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -32,8 +32,8 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/option.h"
+#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
@@ -115,6 +115,8 @@ static int include_none = 0; // when 1 include "nvim/None"
static int include_default = 0; // when 1 include "nvim/default"
static int include_link = 0; // when 2 include "nvim/link" and "clear"
+#define MAX_SYN_NAME 200
+
/// The "term", "cterm" and "gui" arguments can be any combination of the
/// following names, separated by commas (but no spaces!).
static char *(hl_name_table[]) =
@@ -6175,6 +6177,8 @@ static const char *highlight_init_both[] = {
"default link LineNrAbove LineNr",
"default link LineNrBelow LineNr",
"default link QuickFixLine Search",
+ "default link CursorLineSign SignColumn",
+ "default link CursorLineFold FoldColumn",
"default link Substitute Search",
"default link Whitespace NonText",
"default link MsgSeparator StatusLine",
@@ -7623,10 +7627,9 @@ int syn_name2id(const char *name)
int syn_name2id_len(const char_u *name, size_t len)
FUNC_ATTR_NONNULL_ALL
{
- char name_u[201];
+ char name_u[MAX_SYN_NAME + 1];
- if (len == 0 || len > 200) {
- // ID names over 200 chars don't deserve to be found!
+ if (len == 0 || len > MAX_SYN_NAME) {
return 0;
}
@@ -7684,6 +7687,10 @@ char_u *syn_id2name(int id)
/// @return 0 for failure else the id of the group
int syn_check_group(const char *name, int len)
{
+ if (len > MAX_SYN_NAME) {
+ emsg(_(e_highlight_group_name_too_long));
+ return 0;
+ }
int id = syn_name2id_len((char_u *)name, len);
if (id == 0) { // doesn't exist yet
return syn_add_group(vim_strnsave((char_u *)name, len));
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 483d2df778..a10a2a0c32 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -25,11 +25,11 @@
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/if_cscope.h"
+#include "nvim/input.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index c272fdb7b0..6add541ad4 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -64,7 +64,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/option.h"
@@ -1473,6 +1472,17 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
int width, height;
vterm_get_size(term->vt, &height, &width);
+ // May still have pending scrollback after increase in terminal height if the
+ // scrollback wasn't refreshed in time; append these to the top of the buffer.
+ int row_offset = term->sb_pending;
+ while (term->sb_pending > 0 && buf->b_ml.ml_line_count < height) {
+ fetch_row(term, term->sb_pending - row_offset - 1, width);
+ ml_append(0, (uint8_t *)term->textbuf, 0, false);
+ appended_lines(0, 1);
+ term->sb_pending--;
+ }
+
+ row_offset -= term->sb_pending;
while (term->sb_pending > 0) {
// This means that either the window height has decreased or the screen
// became full and libvterm had to push all rows up. Convert the first
@@ -1483,7 +1493,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
ml_delete(1, false);
deleted_lines(1, 1);
}
- fetch_row(term, -term->sb_pending, width);
+ fetch_row(term, -term->sb_pending - row_offset, width);
int buf_index = (int)buf->b_ml.ml_line_count - height;
ml_append(buf_index, (uint8_t *)term->textbuf, 0, false);
appended_lines(buf_index, 1);
@@ -1513,6 +1523,13 @@ static void refresh_screen(Terminal *term, buf_T *buf)
// Terminal height may have decreased before `invalid_end` reflects it.
term->invalid_end = MIN(term->invalid_end, height);
+ // There are no invalid rows.
+ if (term->invalid_start >= term->invalid_end) {
+ term->invalid_start = INT_MAX;
+ term->invalid_end = -1;
+ return;
+ }
+
for (int r = term->invalid_start, linenr = row_to_linenr(term, r);
r < term->invalid_end; r++, linenr++) {
fetch_row(term, r, width);
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index ab047fd2a8..b0d872e392 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -64,6 +64,9 @@ if has('reltime')
let s:start_time = reltime()
endif
+" Always use forward slashes.
+set shellslash
+
" Common with all tests on all systems.
source setup.vim
@@ -104,9 +107,6 @@ lang mess C
" Nvim: append runtime from build dir, which contains the generated doc/tags.
let &runtimepath .= ','.expand($BUILD_DIR).'/runtime/'
-" Always use forward slashes.
-set shellslash
-
let s:t_bold = &t_md
let s:t_normal = &t_me
if has('win32')
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index b3df8c63e6..fdae0697c3 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -13,7 +13,7 @@ set fillchars=vert:\|,fold:-
set laststatus=1
set listchars=eol:$
set joinspaces
-set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd
+set nohidden nosmarttab noautoindent noautoread complete-=i noruler noshowcmd
set nrformats+=octal
set shortmess-=F
set sidescroll=0
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 4e1a24af61..45285b69a1 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -2380,95 +2380,7 @@ func Test_autocmd_was_using_freed_memory()
pclose
endfunc
-func Test_FileChangedShell_reload()
- if !has('unix')
- return
- endif
- augroup testreload
- au FileChangedShell Xchanged let g:reason = v:fcs_reason | let v:fcs_choice = 'reload'
- augroup END
- new Xchanged
- call setline(1, 'reload this')
- write
- " Need to wait until the timestamp would change by at least a second.
- sleep 2
- silent !echo 'extra line' >>Xchanged
- checktime
- call assert_equal('changed', g:reason)
- call assert_equal(2, line('$'))
- call assert_equal('extra line', getline(2))
-
- " Only triggers once
- let g:reason = ''
- checktime
- call assert_equal('', g:reason)
-
- " When deleted buffer is not reloaded
- silent !rm Xchanged
- let g:reason = ''
- checktime
- call assert_equal('deleted', g:reason)
- call assert_equal(2, line('$'))
- call assert_equal('extra line', getline(2))
-
- " When recreated buffer is reloaded
- call setline(1, 'buffer is changed')
- silent !echo 'new line' >>Xchanged
- let g:reason = ''
- checktime
- call assert_equal('conflict', g:reason)
- call assert_equal(1, line('$'))
- call assert_equal('new line', getline(1))
-
- " Only mode changed
- silent !chmod +x Xchanged
- let g:reason = ''
- checktime
- call assert_equal('mode', g:reason)
- call assert_equal(1, line('$'))
- call assert_equal('new line', getline(1))
-
- " Only time changed
- sleep 2
- silent !touch Xchanged
- let g:reason = ''
- checktime
- call assert_equal('time', g:reason)
- call assert_equal(1, line('$'))
- call assert_equal('new line', getline(1))
-
- if has('persistent_undo')
- " With an undo file the reload can be undone and a change before the
- " reload.
- set undofile
- call setline(2, 'before write')
- write
- call setline(2, 'after write')
- sleep 2
- silent !echo 'different line' >>Xchanged
- let g:reason = ''
- checktime
- call assert_equal('conflict', g:reason)
- call assert_equal(3, line('$'))
- call assert_equal('before write', getline(2))
- call assert_equal('different line', getline(3))
- " undo the reload
- undo
- call assert_equal(2, line('$'))
- call assert_equal('after write', getline(2))
- " undo the change before reload
- undo
- call assert_equal(2, line('$'))
- call assert_equal('before write', getline(2))
-
- set noundofile
- endif
-
-
- au! testreload
- bwipe!
- call delete('Xchanged')
-endfunc
+" FileChangedShell tested in test_filechanged.vim
func LogACmd()
call add(g:logged, line('$'))
diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim
index 20758b0c0a..af42b3857d 100644
--- a/src/nvim/testdir/test_blob.vim
+++ b/src/nvim/testdir/test_blob.vim
@@ -346,4 +346,12 @@ func Test_blob_sort()
endif
endfunc
+" The following used to cause an out-of-bounds memory access
+func Test_blob2string()
+ let v = '0z' .. repeat('01010101.', 444)
+ let v ..= '01'
+ exe 'let b = ' .. v
+ call assert_equal(v, string(b))
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 49a5386337..1672b0e840 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -467,6 +467,43 @@ func Test_getcompletion()
call assert_fails('call getcompletion("abc", [])', 'E475:')
endfunc
+func Test_fullcommand()
+ let tests = {
+ \ '': '',
+ \ ':': '',
+ \ ':::': '',
+ \ ':::5': '',
+ \ 'not_a_cmd': '',
+ \ 'Check': '',
+ \ 'syntax': 'syntax',
+ \ ':syntax': 'syntax',
+ \ '::::syntax': 'syntax',
+ \ 'sy': 'syntax',
+ \ 'syn': 'syntax',
+ \ 'synt': 'syntax',
+ \ ':sy': 'syntax',
+ \ '::::sy': 'syntax',
+ \ 'match': 'match',
+ \ '2match': 'match',
+ \ '3match': 'match',
+ \ 'aboveleft': 'aboveleft',
+ \ 'abo': 'aboveleft',
+ \ 's': 'substitute',
+ \ '5s': 'substitute',
+ \ ':5s': 'substitute',
+ \ "'<,'>s": 'substitute',
+ \ ":'<,'>s": 'substitute',
+ \ 'CheckUni': 'CheckUnix',
+ \ 'CheckUnix': 'CheckUnix',
+ \ }
+
+ for [in, want] in items(tests)
+ call assert_equal(want, fullcommand(in))
+ endfor
+
+ call assert_equal('syntax', 'syn'->fullcommand())
+endfunc
+
func Test_shellcmd_completion()
let save_path = $PATH
diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim
index 12327f34d6..c2a9683f7c 100644
--- a/src/nvim/testdir/test_display.vim
+++ b/src/nvim/testdir/test_display.vim
@@ -263,6 +263,31 @@ func Test_display_scroll_at_topline()
call StopVimInTerminal(buf)
endfunc
+" Test for 'eob' (EndOfBuffer) item in 'fillchars'
+func Test_eob_fillchars()
+ " default value (skipped)
+ " call assert_match('eob:\~', &fillchars)
+ " invalid values
+ call assert_fails(':set fillchars=eob:', 'E474:')
+ call assert_fails(':set fillchars=eob:xy', 'E474:')
+ call assert_fails(':set fillchars=eob:\255', 'E474:')
+ call assert_fails(':set fillchars=eob:<ff>', 'E474:')
+ call assert_fails(":set fillchars=eob:\x01", 'E474:')
+ call assert_fails(':set fillchars=eob:\\x01', 'E474:')
+ " default is ~
+ new
+ redraw
+ call assert_equal('~', Screenline(2))
+ set fillchars=eob:+
+ redraw
+ call assert_equal('+', Screenline(2))
+ set fillchars=eob:\
+ redraw
+ call assert_equal(' ', nr2char(screenchar(2, 1)))
+ set fillchars&
+ close
+endfunc
+
func Test_display_linebreak_breakat()
new
vert resize 25
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 37786f3ca0..fc4e80f0d6 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1294,6 +1294,7 @@ func Test_edit_forbidden()
call assert_fails(':Sandbox', 'E48:')
delcom Sandbox
call assert_equal(['a'], getline(1,'$'))
+
" 2) edit with textlock set
fu! DoIt()
call feedkeys("i\<del>\<esc>", 'tnix')
@@ -1313,6 +1314,7 @@ func Test_edit_forbidden()
catch /^Vim\%((\a\+)\)\=:E117/ " catch E117: unknown function
endtry
au! InsertCharPre
+
" 3) edit when completion is shown
fun! Complete(findstart, base)
if a:findstart
@@ -1330,6 +1332,7 @@ func Test_edit_forbidden()
endtry
delfu Complete
set completefunc=
+
if has("rightleft") && exists("+fkmap")
" 4) 'R' when 'fkmap' and 'revins' is set.
set revins fkmap
diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim
index 1c645ad0f8..92e0559618 100644
--- a/src/nvim/testdir/test_ex_mode.vim
+++ b/src/nvim/testdir/test_ex_mode.vim
@@ -85,7 +85,7 @@ endfunc
func Test_ex_mode_count_overflow()
" this used to cause a crash
let lines =<< trim END
- call feedkeys("\<Esc>Q\<CR>")
+ call feedkeys("\<Esc>gQ\<CR>")
v9|9silent! vi|333333233333y32333333%O
call writefile(['done'], 'Xdidexmode')
qall!
diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim
new file mode 100644
index 0000000000..b95cd5faf8
--- /dev/null
+++ b/src/nvim/testdir/test_filechanged.vim
@@ -0,0 +1,149 @@
+" Tests for when a file was changed outside of Vim.
+
+func Test_FileChangedShell_reload()
+ if !has('unix')
+ return
+ endif
+ augroup testreload
+ au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'reload'
+ augroup END
+ new Xchanged_r
+ call setline(1, 'reload this')
+ write
+ " Need to wait until the timestamp would change by at least a second.
+ sleep 2
+ silent !echo 'extra line' >>Xchanged_r
+ checktime
+ call assert_equal('changed', g:reason)
+ call assert_equal(2, line('$'))
+ call assert_equal('extra line', getline(2))
+
+ " Only triggers once
+ let g:reason = ''
+ checktime
+ call assert_equal('', g:reason)
+
+ " When deleted buffer is not reloaded
+ silent !rm Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('deleted', g:reason)
+ call assert_equal(2, line('$'))
+ call assert_equal('extra line', getline(2))
+
+ " When recreated buffer is reloaded
+ call setline(1, 'buffer is changed')
+ silent !echo 'new line' >>Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('conflict', g:reason)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only mode changed
+ silent !chmod +x Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('mode', g:reason)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only time changed
+ sleep 2
+ silent !touch Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('time', g:reason)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ if has('persistent_undo')
+ " With an undo file the reload can be undone and a change before the
+ " reload.
+ set undofile
+ call setline(2, 'before write')
+ write
+ call setline(2, 'after write')
+ sleep 2
+ silent !echo 'different line' >>Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('conflict', g:reason)
+ call assert_equal(3, line('$'))
+ call assert_equal('before write', getline(2))
+ call assert_equal('different line', getline(3))
+ " undo the reload
+ undo
+ call assert_equal(2, line('$'))
+ call assert_equal('after write', getline(2))
+ " undo the change before reload
+ undo
+ call assert_equal(2, line('$'))
+ call assert_equal('before write', getline(2))
+
+ set noundofile
+ endif
+
+ au! testreload
+ bwipe!
+ call delete(undofile('Xchanged_r'))
+ call delete('Xchanged_r')
+endfunc
+
+func Test_file_changed_dialog()
+ throw 'skipped: TODO: '
+ if !has('unix') || has('gui_running')
+ return
+ endif
+ au! FileChangedShell
+
+ new Xchanged_d
+ call setline(1, 'reload this')
+ write
+ " Need to wait until the timestamp would change by at least a second.
+ sleep 2
+ silent !echo 'extra line' >>Xchanged_d
+ call feedkeys('L', 'L')
+ checktime
+ call assert_match('W11:', v:warningmsg)
+ call assert_equal(2, line('$'))
+ call assert_equal('reload this', getline(1))
+ call assert_equal('extra line', getline(2))
+
+ " delete buffer, only shows an error, no prompt
+ silent !rm Xchanged_d
+ checktime
+ call assert_match('E211:', v:warningmsg)
+ call assert_equal(2, line('$'))
+ call assert_equal('extra line', getline(2))
+ let v:warningmsg = 'empty'
+
+ " change buffer, recreate the file and reload
+ call setline(1, 'buffer is changed')
+ silent !echo 'new line' >Xchanged_d
+ call feedkeys('L', 'L')
+ checktime
+ call assert_match('W12:', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only mode changed, reload
+ silent !chmod +x Xchanged_d
+ call feedkeys('L', 'L')
+ checktime
+ call assert_match('W16:', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only time changed, no prompt
+ sleep 2
+ silent !touch Xchanged_d
+ let v:warningmsg = ''
+ checktime
+ call assert_equal('', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ bwipe!
+ call delete('Xchanged_d')
+endfunc
diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim
index 465613f1cf..81127ea59a 100644
--- a/src/nvim/testdir/test_fileformat.vim
+++ b/src/nvim/testdir/test_fileformat.vim
@@ -1,5 +1,4 @@
" Test behavior of fileformat after bwipeout of last buffer
-
func Test_fileformat_after_bw()
bwipeout
set fileformat&
@@ -32,6 +31,251 @@ func Test_fileformat_autocommand()
bw!
endfunc
+" Convert the contents of a file into a literal string
+func s:file2str(fname)
+ let b = readfile(a:fname, 'B')
+ let s = ''
+ for c in b
+ let s .= nr2char(c)
+ endfor
+ return s
+endfunc
+
+" Concatenate the contents of files 'f1' and 'f2' and create 'destfile'
+func s:concat_files(f1, f2, destfile)
+ let b1 = readfile(a:f1, 'B')
+ let b2 = readfile(a:f2, 'B')
+ let b3 = b1 + b2
+ call writefile(b3, a:destfile)
+endfun
+
+" Test for a lot of variations of the 'fileformats' option
+func Test_fileformats()
+ " create three test files, one in each format
+ call writefile(['unix', 'unix'], 'XXUnix')
+ call writefile(["dos\r", "dos\r"], 'XXDos')
+ call writefile(["mac\rmac\r"], 'XXMac', 'b')
+ " create a file with no End Of Line
+ call writefile(["noeol"], 'XXEol', 'b')
+ " create mixed format files
+ call s:concat_files('XXUnix', 'XXDos', 'XXUxDs')
+ call s:concat_files('XXUnix', 'XXMac', 'XXUxMac')
+ call s:concat_files('XXDos', 'XXMac', 'XXDosMac')
+ call s:concat_files('XXMac', 'XXEol', 'XXMacEol')
+ call s:concat_files('XXUxDs', 'XXMac', 'XXUxDsMc')
+
+ new
+
+ " Test 1: try reading and writing with 'fileformats' empty
+ set fileformats=
+
+ " try with 'fileformat' set to 'unix'
+ set fileformat=unix
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\nunix\n", s:file2str('Xtest'))
+ e! XXDos
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\n", s:file2str('Xtest'))
+ e! XXMac
+ w! Xtest
+ call assert_equal("mac\rmac\r\n", s:file2str('Xtest'))
+ bwipe XXUnix XXDos XXMac
+
+ " try with 'fileformat' set to 'dos'
+ set fileformat=dos
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\n", s:file2str('Xtest'))
+ e! XXDos
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\n", s:file2str('Xtest'))
+ e! XXMac
+ w! Xtest
+ call assert_equal("mac\rmac\r\r\n", s:file2str('Xtest'))
+ bwipe XXUnix XXDos XXMac
+
+ " try with 'fileformat' set to 'mac'
+ set fileformat=mac
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\nunix\n\r", s:file2str('Xtest'))
+ e! XXDos
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\n\r", s:file2str('Xtest'))
+ e! XXMac
+ w! Xtest
+ call assert_equal("mac\rmac\r", s:file2str('Xtest'))
+ bwipe XXUnix XXDos XXMac
+
+ " Test 2: try reading and writing with 'fileformats' set to one format
+
+ " try with 'fileformats' set to 'unix'
+ set fileformats=unix
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ " try with 'fileformats' set to 'dos'
+ set fileformats=dos
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\ndos\r\ndos\r\nmac\rmac\r\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ " try with 'fileformats' set to 'mac'
+ set fileformats=mac
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ " Test 3: try reading and writing with 'fileformats' set to two formats
+
+ " try with 'fileformats' set to 'unix,dos'
+ set fileformats=unix,dos
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXUxMac
+ w! Xtest
+ call assert_equal("unix\nunix\nmac\rmac\r\n", s:file2str('Xtest'))
+ bwipe XXUxMac
+
+ e! XXDosMac
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\nmac\rmac\r\r\n", s:file2str('Xtest'))
+ bwipe XXDosMac
+
+ " try with 'fileformats' set to 'unix,mac'
+ set fileformats=unix,mac
+ e! XXUxDs
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\n", s:file2str('Xtest'))
+ bwipe XXUxDs
+
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXDosMac
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\nmac\rmac\r", s:file2str('Xtest'))
+ bwipe XXDosMac
+
+ e! XXEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("unix,mac:unix\nnoeol\n", s:file2str('Xtest'))
+ bwipe! XXEol
+
+ " try with 'fileformats' set to 'dos,mac'
+ set fileformats=dos,mac
+ e! XXUxDs
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\ndos\r\ndos\r\n", s:file2str('Xtest'))
+ bwipe XXUxDs
+
+ e! XXUxMac
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("dos,mac:dos\r\nunix\r\nunix\r\nmac\rmac\r\r\n",
+ \ s:file2str('Xtest'))
+ bwipe! XXUxMac
+
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\ndos\r\ndos\r\nmac\rmac\r\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXMacEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("dos,mac:mac\rmac\rmac\rnoeol\r", s:file2str('Xtest'))
+ bwipe! XXMacEol
+
+ " Test 4: try reading and writing with 'fileformats' set to three formats
+ set fileformats=unix,dos,mac
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("unix,dos,mac:unix\nnoeol\n", s:file2str('Xtest'))
+ bwipe! XXEol
+
+ set fileformats=mac,dos,unix
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("mac,dos,unix:mac\rnoeol\r", s:file2str('Xtest'))
+ bwipe! XXEol
+
+ " Test 5: try with 'binary' set
+ set fileformats=mac,unix,dos
+ set binary
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ set fileformats=mac
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ set fileformats=dos
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\nunix\n", s:file2str('Xtest'))
+ bwipe! XXUnix
+
+ set nobinary ff& ffs&
+
+ " cleanup
+ only
+ %bwipe!
+ call delete('XXUnix')
+ call delete('XXDos')
+ call delete('XXMac')
+ call delete('XXEol')
+ call delete('XXUxDs')
+ call delete('XXUxMac')
+ call delete('XXDosMac')
+ call delete('XXMacEol')
+ call delete('XXUxDsMc')
+ call delete('Xtest')
+endfunc
+
" Test for changing the fileformat using ++read
func Test_fileformat_plusplus_read()
new
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 69edbc227d..1ee23bb646 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -117,7 +117,7 @@ let s:filename_checks = {
\ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh'],
\ 'crm': ['file.crm'],
\ 'crontab': ['crontab', 'crontab.file', '/etc/cron.d/file', 'any/etc/cron.d/file'],
- \ 'cs': ['file.cs'],
+ \ 'cs': ['file.cs', 'file.csx'],
\ 'csc': ['file.csc'],
\ 'csdl': ['file.csdl'],
\ 'csp': ['file.csp', 'file.fdr'],
@@ -204,6 +204,7 @@ let s:filename_checks = {
\ 'gnash': ['gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'],
\ 'gnuplot': ['file.gpi'],
\ 'go': ['file.go'],
+ \ 'gomod': ['go.mod'],
\ 'gp': ['file.gp', '.gprc'],
\ 'gpg': ['/.gnupg/options', '/.gnupg/gpg.conf', '/usr/any/gnupg/options.skel', 'any/.gnupg/gpg.conf', 'any/.gnupg/options', 'any/usr/any/gnupg/options.skel'],
\ 'grads': ['file.gs'],
@@ -226,6 +227,7 @@ let s:filename_checks = {
\ 'hollywood': ['file.hws'],
\ 'hostconf': ['/etc/host.conf', 'any/etc/host.conf'],
\ 'hostsaccess': ['/etc/hosts.allow', '/etc/hosts.deny', 'any/etc/hosts.allow', 'any/etc/hosts.deny'],
+ \ 'i3config': ['/home/user/.i3/config', '/home/user/.config/i3/config', '/etc/i3/config', '/etc/xdg/i3/config'],
\ 'logcheck': ['/etc/logcheck/file.d-some/file', '/etc/logcheck/file.d/file', 'any/etc/logcheck/file.d-some/file', 'any/etc/logcheck/file.d/file'],
\ 'modula3': ['file.m3', 'file.mg', 'file.i3', 'file.ig'],
\ 'natural': ['file.NSA', 'file.NSC', 'file.NSG', 'file.NSL', 'file.NSM', 'file.NSN', 'file.NSP', 'file.NSS'],
@@ -261,7 +263,7 @@ let s:filename_checks = {
\ 'jgraph': ['file.jgr'],
\ 'jovial': ['file.jov', 'file.j73', 'file.jovial'],
\ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'],
- \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc'],
+ \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc', 'file.slnf'],
\ 'jsonc': ['file.jsonc'],
\ 'jsp': ['file.jsp'],
\ 'julia': ['file.jl'],
@@ -455,6 +457,7 @@ let s:filename_checks = {
\ 'skill': ['file.il', 'file.ils', 'file.cdf'],
\ 'slang': ['file.sl'],
\ 'slice': ['file.ice'],
+ \ 'solution': ['file.sln'],
\ 'slpconf': ['/etc/slp.conf', 'any/etc/slp.conf'],
\ 'slpreg': ['/etc/slp.reg', 'any/etc/slp.reg'],
\ 'slpspi': ['/etc/slp.spi', 'any/etc/slp.spi'],
@@ -502,7 +505,7 @@ let s:filename_checks = {
\ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'],
\ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'],
\ 'texmf': ['texmf.cnf'],
- \ 'text': ['file.text', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'],
+ \ 'text': ['file.text', 'file.txt', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'],
\ 'tf': ['file.tf', '.tfrc', 'tfrc'],
\ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'],
\ 'tilde': ['file.t.html'],
@@ -568,6 +571,7 @@ let s:filename_checks = {
\ 'yaml': ['file.yaml', 'file.yml'],
\ 'raml': ['file.raml'],
\ 'z8a': ['file.z8a'],
+ \ 'zig': ['file.zig'],
\ 'zimbu': ['file.zu'],
\ 'zimbutempl': ['file.zut'],
\ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh', '.zcompdump', '.zlogin', '.zlogout', '.zshenv', '.zshrc', '.zcompdump-file', '.zlog', '.zlog-file', '.zsh', '.zsh-file', 'any/etc/zprofile', 'zlog', 'zlog-file', 'zsh', 'zsh-file'],
@@ -1001,4 +1005,44 @@ func Test_fs_file()
filetype off
endfunc
+func Test_dep3patch_file()
+ filetype on
+
+ call assert_true(mkdir('debian/patches', 'p'))
+
+ " series files are not patches
+ call writefile(['Description: some awesome patch'], 'debian/patches/series')
+ split debian/patches/series
+ call assert_notequal('dep3patch', &filetype)
+ bwipe!
+
+ " diff/patch files without the right headers should still show up as ft=diff
+ call writefile([], 'debian/patches/foo.diff')
+ split debian/patches/foo.diff
+ call assert_equal('diff', &filetype)
+ bwipe!
+
+ " Files with the right headers are detected as dep3patch, even if they don't
+ " have a diff/patch extension
+ call writefile(['Subject: dep3patches'], 'debian/patches/bar')
+ split debian/patches/bar
+ call assert_equal('dep3patch', &filetype)
+ bwipe!
+
+ " Files in sub-directories are detected
+ call assert_true(mkdir('debian/patches/s390x', 'p'))
+ call writefile(['Subject: dep3patches'], 'debian/patches/s390x/bar')
+ split debian/patches/s390x/bar
+ call assert_equal('dep3patch', &filetype)
+ bwipe!
+
+ " The detection stops when seeing the "header end" marker
+ call writefile(['---', 'Origin: the cloud'], 'debian/patches/baz')
+ split debian/patches/baz
+ call assert_notequal('dep3patch', &filetype)
+ bwipe!
+
+ call delete('debian/patches', 'rf')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index c38bfa5677..899eb530ec 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -661,6 +661,13 @@ function Test_no_space_before_xxx()
let &columns = l:org_columns
endfunction
+" Test for :highlight command errors
+func Test_highlight_cmd_errors()
+ if has('gui_running') || has('nvim')
+ call assert_fails('hi ' .. repeat('a', 201) .. ' ctermfg=black', 'E1249:')
+ endif
+endfunc
+
" Test for using RGB color values in a highlight group
func Test_xxlast_highlight_RGB_color()
CheckCanRunGui
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index f4ee539803..0bcbd9c4a5 100644
--- a/src/nvim/testdir/test_listchars.vim
+++ b/src/nvim/testdir/test_listchars.vim
@@ -331,7 +331,7 @@ func Test_listchars_invalid()
call assert_fails('set listchars=space:xx', 'E474:')
call assert_fails('set listchars=tab:xxxx', 'E474:')
- " Has non-single width character
+ " Has double-width character
call assert_fails('set listchars=space:·', 'E474:')
call assert_fails('set listchars=tab:·x', 'E474:')
call assert_fails('set listchars=tab:x·', 'E474:')
@@ -339,6 +339,20 @@ func Test_listchars_invalid()
call assert_fails('set listchars=multispace:·', 'E474:')
call assert_fails('set listchars=multispace:xxx·', 'E474:')
+ " Has control character
+ call assert_fails("set listchars=space:\x01", 'E474:')
+ call assert_fails("set listchars=tab:\x01x", 'E474:')
+ call assert_fails("set listchars=tab:x\x01", 'E474:')
+ call assert_fails("set listchars=tab:xx\x01", 'E474:')
+ call assert_fails("set listchars=multispace:\x01", 'E474:')
+ call assert_fails("set listchars=multispace:xxx\x01", 'E474:')
+ call assert_fails('set listchars=space:\\x01', 'E474:')
+ call assert_fails('set listchars=tab:\\x01x', 'E474:')
+ call assert_fails('set listchars=tab:x\\x01', 'E474:')
+ call assert_fails('set listchars=tab:xx\\x01', 'E474:')
+ call assert_fails('set listchars=multispace:\\x01', 'E474:')
+ call assert_fails('set listchars=multispace:xxx\\x01', 'E474:')
+
enew!
set ambiwidth& listchars& ff&
endfunction
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index 2140fe21ea..e0286548d9 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -108,3 +108,11 @@ func Test_echospace()
set ruler& showcmd&
endfunc
+
+" this was missing a terminating NUL
+func Test_echo_string_partial()
+ function CountSpaces()
+ endfunction
+ call assert_equal("function('CountSpaces', [{'ccccccccccc': ['ab', 'cd'], 'aaaaaaaaaaa': v:false, 'bbbbbbbbbbbb': ''}])", string(function('CountSpaces', [#{aaaaaaaaaaa: v:false, bbbbbbbbbbbb: '', ccccccccccc: ['ab', 'cd']}])))
+endfunc
+
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 7d9cada074..5946732937 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -732,4 +732,25 @@ func Test_opt_reset_scroll()
call delete('Xscroll')
endfunc
+" Test for the 'cdhome' option
+func Test_opt_cdhome()
+ if has('unix') || has('vms')
+ throw 'Skipped: only works on non-Unix'
+ endif
+
+ set cdhome&
+ call assert_equal(0, &cdhome)
+ set cdhome
+
+ " This paragraph is copied from Test_cd_no_arg().
+ let path = getcwd()
+ cd
+ call assert_equal($HOME, getcwd())
+ call assert_notequal(path, getcwd())
+ exe 'cd ' .. fnameescape(path)
+ call assert_equal(path, getcwd())
+
+ set cdhome&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index f287256396..ff9ba3d8ed 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -15,13 +15,13 @@ func Test_sign()
" the icon name when listing signs.
sign define Sign1 text=x
- call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error icon=../../pixmaps/stock_vim_find_help.png')
+ call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error culhl=Search icon=../../pixmaps/stock_vim_find_help.png')
" Test listing signs.
let a=execute('sign list')
call assert_match('^\nsign Sign1 text=x \nsign Sign2 ' .
\ 'icon=../../pixmaps/stock_vim_find_help.png .*text=xy ' .
- \ 'linehl=Error texthl=Title$', a)
+ \ 'linehl=Error texthl=Title culhl=Search$', a)
let a=execute('sign list Sign1')
call assert_equal("\nsign Sign1 text=x ", a)
@@ -126,6 +126,30 @@ func Test_sign()
" call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:')
call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:')
+ " an empty highlight argument for an existing sign clears it
+ sign define SignY texthl=TextHl culhl=CulHl linehl=LineHl
+ let sl = sign_getdefined('SignY')[0]
+ call assert_equal('TextHl', sl.texthl)
+ call assert_equal('CulHl', sl.culhl)
+ call assert_equal('LineHl', sl.linehl)
+
+ sign define SignY texthl= culhl=CulHl linehl=LineHl
+ let sl = sign_getdefined('SignY')[0]
+ call assert_false(has_key(sl, 'texthl'))
+ call assert_equal('CulHl', sl.culhl)
+ call assert_equal('LineHl', sl.linehl)
+
+ sign define SignY linehl=
+ let sl = sign_getdefined('SignY')[0]
+ call assert_false(has_key(sl, 'linehl'))
+ call assert_equal('CulHl', sl.culhl)
+
+ sign define SignY culhl=
+ let sl = sign_getdefined('SignY')[0]
+ call assert_false(has_key(sl, 'culhl'))
+
+ sign undefine SignY
+
" define sign with whitespace
sign define Sign4 text=\ X linehl=Comment
sign undefine Sign4
@@ -194,15 +218,13 @@ func Test_sign_completion()
call assert_equal('"sign define jump list place undefine unplace', @:)
call feedkeys(":sign define Sign \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"sign define Sign icon= linehl= numhl= text= texthl=', @:)
-
- call feedkeys(":sign define Sign linehl=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"sign define Sign linehl=SpellBad SpellCap ' .
- \ 'SpellLocal SpellRare', @:)
+ call assert_equal('"sign define Sign culhl= icon= linehl= numhl= text= texthl=', @:)
- call feedkeys(":sign define Sign texthl=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"sign define Sign texthl=SpellBad SpellCap ' .
- \ 'SpellLocal SpellRare', @:)
+ for hl in ['culhl', 'linehl', 'numhl', 'texthl']
+ call feedkeys(":sign define Sign "..hl.."=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"sign define Sign '..hl..'=SpellBad SpellCap ' .
+ \ 'SpellLocal SpellRare', @:)
+ endfor
call writefile(repeat(["Sun is shining"], 30), "XsignOne")
call writefile(repeat(["Sky is blue"], 30), "XsignTwo")
@@ -392,19 +414,22 @@ func Test_sign_funcs()
call sign_undefine()
" Tests for sign_define()
- let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'}
+ let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error',
+ \ 'culhl': 'Visual', 'numhl': 'Number'}
call assert_equal(0, "sign1"->sign_define(attr))
- call assert_equal([{'name' : 'sign1', 'texthl' : 'Error',
- \ 'linehl' : 'Search', 'text' : '=>'}], sign_getdefined())
+ call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', 'linehl': 'Search',
+ \ 'culhl': 'Visual', 'numhl': 'Number', 'text' : '=>'}],
+ \ sign_getdefined())
" Define a new sign without attributes and then update it
call sign_define("sign2")
let attr = {'text' : '!!', 'linehl' : 'DiffAdd', 'texthl' : 'DiffChange',
- \ 'icon' : 'sign2.ico'}
+ \ 'culhl': 'DiffDelete', 'numhl': 'Number', 'icon' : 'sign2.ico'}
call Sign_define_ignore_error("sign2", attr)
call assert_equal([{'name' : 'sign2', 'texthl' : 'DiffChange',
- \ 'linehl' : 'DiffAdd', 'text' : '!!', 'icon' : 'sign2.ico'}],
- \ "sign2"->sign_getdefined())
+ \ 'linehl' : 'DiffAdd', 'culhl' : 'DiffDelete', 'text' : '!!',
+ \ 'numhl': 'Number', 'icon' : 'sign2.ico'}],
+ \ "sign2"->sign_getdefined())
" Test for a sign name with digits
call assert_equal(0, sign_define(0002, {'linehl' : 'StatusLine'}))
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index 113c85acef..20b760ac15 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -137,7 +137,7 @@ func Test_substitute_repeat()
" This caused an invalid memory access.
split Xfile
s/^/x
- call feedkeys("Qsc\<CR>y", 'tx')
+ call feedkeys("gQsc\<CR>y", 'tx')
bwipe!
endfunc
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index 5cc0da2586..aae315b2c5 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -279,7 +279,7 @@ func Test_ex_mode()
endfunc
let timer = timer_start(40, function('g:Foo'), {'repeat':-1})
" This used to throw error E749.
- exe "normal Qsleep 100m\rvi\r"
+ exe "normal gQsleep 100m\rvi\r"
call timer_stop(timer)
endfunc
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index dbabdcf427..8344598486 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -144,7 +144,6 @@ endfun
" Test Virtual replace mode.
func Test_virtual_replace()
- throw 'skipped: TODO: '
if exists('&t_kD')
let save_t_kD = &t_kD
endif
@@ -166,7 +165,6 @@ func Test_virtual_replace()
\ ], getline(1, 6))
normal G
mark a
- inoremap <C-D> <Del>
exe "normal o0\<C-D>\nabcdefghi\njk\tlmn\n opq\trst\n\<C-D>uvwxyz\n"
exe "normal 'ajgR0\<C-D> 1\nA\nBCDEFGHIJ\n\tKL\nMNO\nPQR" . repeat("\<BS>", 29)
call assert_equal([' 1',
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 5bb6059fa7..5fec41f9a5 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -114,6 +114,12 @@ static void tinput_done_event(void **argv)
static void tinput_wait_enqueue(void **argv)
{
TermInput *input = argv[0];
+ if (rbuffer_size(input->key_buffer) == 0 && input->paste == 3) {
+ const String keys = { .data = "", .size = 0 };
+ String copy = copy_string(keys);
+ multiqueue_put(main_loop.events, tinput_paste_event, 3,
+ copy.data, copy.size, (intptr_t)input->paste);
+ }
RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
const String keys = { .data = buf, .size = len };
if (input->paste) {
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index bb75286369..e7a60aca49 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -308,23 +308,39 @@ static void terminfo_start(UI *ui)
// Enable bracketed paste
unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste);
+ int ret;
uv_loop_init(&data->write_loop);
if (data->out_isatty) {
- uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0);
+ ret = uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0);
+ if (ret) {
+ ELOG("uv_tty_init failed: %s", uv_strerror(ret));
+ }
#ifdef WIN32
- uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW);
+ ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW);
+ if (ret) {
+ ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret));
+ }
#else
int retry_count = 10;
// A signal may cause uv_tty_set_mode() to fail (e.g., SIGCONT). Retry a
// few times. #12322
- while (uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO) == UV_EINTR
+ while ((ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO)) == UV_EINTR
&& retry_count > 0) {
retry_count--;
}
+ if (ret) {
+ ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret));
+ }
#endif
} else {
- uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0);
- uv_pipe_open(&data->output_handle.pipe, data->out_fd);
+ ret = uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0);
+ if (ret) {
+ ELOG("uv_pipe_init failed: %s", uv_strerror(ret));
+ }
+ ret = uv_pipe_open(&data->output_handle.pipe, data->out_fd);
+ if (ret) {
+ ELOG("uv_pipe_open failed: %s", uv_strerror(ret));
+ }
}
flush_buf(ui);
}
@@ -1086,8 +1102,14 @@ static void tui_mode_change(UI *ui, String mode, Integer mode_idx)
// after calling uv_tty_set_mode. So, set the mode of the TTY again here.
// #13073
if (data->is_starting && data->input.in_fd == STDERR_FILENO) {
- uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_NORMAL);
- uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO);
+ int ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_NORMAL);
+ if (ret) {
+ ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret));
+ }
+ ret = uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO);
+ if (ret) {
+ ELOG("uv_tty_set_mode failed: %s", uv_strerror(ret));
+ }
}
#endif
tui_set_mode(ui, (ModeShape)mode_idx);
@@ -2081,8 +2103,11 @@ static void flush_buf(UI *ui)
fwrite(bufs[i].base, bufs[i].len, 1, data->screenshot);
}
} else {
- uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle),
- bufs, (unsigned)(bufp - bufs), NULL);
+ int ret = uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle),
+ bufs, (unsigned)(bufp - bufs), NULL);
+ if (ret) {
+ ELOG("uv_write failed: %s", uv_strerror(ret));
+ }
uv_run(&data->write_loop, UV_RUN_DEFAULT);
}
data->bufpos = 0;
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index aad72af025..1aadaf5c9d 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -23,7 +23,6 @@
#include "nvim/main.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
-#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
@@ -300,6 +299,44 @@ void ui_busy_stop(void)
}
}
+/// Emit a bell or visualbell as a warning
+///
+/// val is one of the BO_ values, e.g., BO_OPER
+void vim_beep(unsigned val)
+{
+ called_vim_beep = true;
+
+ if (emsg_silent == 0) {
+ if (!((bo_flags & val) || (bo_flags & BO_ALL))) {
+ static int beeps = 0;
+ static uint64_t start_time = 0;
+
+ // Only beep up to three times per half a second,
+ // otherwise a sequence of beeps would freeze Vim.
+ if (start_time == 0 || os_hrtime() - start_time > 500000000u) {
+ beeps = 0;
+ start_time = os_hrtime();
+ }
+ beeps++;
+ if (beeps <= 3) {
+ if (p_vb) {
+ ui_call_visual_bell();
+ } else {
+ ui_call_bell();
+ }
+ }
+ }
+
+ // When 'debug' contains "beep" produce a message. If we are sourcing
+ // a script or executing a function give the user a hint where the beep
+ // comes from.
+ if (vim_strchr(p_debug, 'e') != NULL) {
+ msg_source(HL_ATTR(HLF_W));
+ msg_attr(_("Beep!"), HL_ATTR(HLF_W));
+ }
+ }
+}
+
void ui_attach_impl(UI *ui, uint64_t chanid)
{
if (ui_count == MAX_UI_COUNT) {
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 8161fce9f4..d18f35a43a 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -93,13 +93,14 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
+#include "nvim/getchar.h"
#include "nvim/lib/kvec.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/option.h"
+#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
@@ -2621,7 +2622,7 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
if (uhp == NULL) {
*msgbuf = NUL;
} else {
- add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
+ undo_fmt_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
}
{
@@ -2641,6 +2642,29 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
msgbuf);
}
+/// Put the timestamp of an undo header in "buf[buflen]" in a nice format.
+void undo_fmt_time(char_u *buf, size_t buflen, time_t tt)
+{
+ struct tm curtime;
+
+ if (time(NULL) - tt >= 100) {
+ os_localtime_r(&tt, &curtime);
+ if (time(NULL) - tt < (60L * 60L * 12L)) {
+ // within 12 hours
+ (void)strftime((char *)buf, buflen, "%H:%M:%S", &curtime);
+ } else {
+ // longer ago
+ (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", &curtime);
+ }
+ } else {
+ int64_t seconds = time(NULL) - tt;
+ vim_snprintf((char *)buf, buflen,
+ NGETTEXT("%" PRId64 " second ago",
+ "%" PRId64 " seconds ago", (uint32_t)seconds),
+ seconds);
+ }
+}
+
/// u_sync: stop adding to the current entry list
///
/// @param force if true, also sync when no_u_sync is set.
@@ -2683,16 +2707,13 @@ void ex_undolist(exarg_T *eap)
while (uhp != NULL) {
if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
&& uhp->uh_walk != mark) {
- vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ",
- uhp->uh_seq, changes);
- add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
- uhp->uh_time);
+ vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ", uhp->uh_seq, changes);
+ undo_fmt_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), uhp->uh_time);
if (uhp->uh_save_nr > 0) {
while (STRLEN(IObuff) < 33) {
STRCAT(IObuff, " ");
}
- vim_snprintf_add((char *)IObuff, IOSIZE,
- " %3ld", uhp->uh_save_nr);
+ vim_snprintf_add((char *)IObuff, IOSIZE, " %3ld", uhp->uh_save_nr);
}
GA_APPEND(char_u *, &ga, vim_strsave(IObuff));
}
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index e3539c1a57..2f8ddd1e88 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -215,6 +215,11 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
// (vim_strchr() is now in strings.c)
#define STRLEN(s) strlen((char *)(s))
+#ifdef HAVE_STRNLEN
+# define STRNLEN(s, n) strnlen((char *)(s), (size_t)(n))
+#else
+# define STRNLEN(s, n) xstrnlen((char *)(s), (size_t)(n))
+#endif
#define STRCPY(d, s) strcpy((char *)(d), (char *)(s))
#define STRNCPY(d, s, n) strncpy((char *)(d), (char *)(s), (size_t)(n))
#define STRLCPY(d, s, n) xstrlcpy((char *)(d), (char *)(s), (size_t)(n))
diff --git a/src/nvim/window.c b/src/nvim/window.c
index be963d8374..c711f462d1 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -30,7 +30,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/misc1.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"