aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.clang-format8
-rw-r--r--src/nvim/CMakeLists.txt58
-rw-r--r--src/nvim/README.md26
-rw-r--r--src/nvim/api/buffer.c273
-rw-r--r--src/nvim/api/private/defs.h1
-rw-r--r--src/nvim/api/private/helpers.c36
-rw-r--r--src/nvim/api/tabpage.c29
-rw-r--r--src/nvim/api/ui.c (renamed from src/nvim/msgpack_rpc/remote_ui.c)90
-rw-r--r--src/nvim/api/ui.h11
-rw-r--r--src/nvim/api/vim.c38
-rw-r--r--src/nvim/api/window.c33
-rw-r--r--src/nvim/arabic.c18
-rw-r--r--src/nvim/assert.h108
-rw-r--r--src/nvim/auevents.lua1
-rw-r--r--src/nvim/buffer.c562
-rw-r--r--src/nvim/buffer_defs.h251
-rw-r--r--src/nvim/bufhl_defs.h25
-rw-r--r--src/nvim/charset.c257
-rw-r--r--src/nvim/diff.c249
-rw-r--r--src/nvim/edit.c1044
-rw-r--r--src/nvim/edit.h6
-rw-r--r--src/nvim/eval.c3561
-rw-r--r--src/nvim/eval.h46
-rw-r--r--src/nvim/eval/decode.c1116
-rw-r--r--src/nvim/eval/decode.h13
-rw-r--r--src/nvim/eval/encode.c1296
-rw-r--r--src/nvim/eval/encode.h75
-rw-r--r--src/nvim/eval_defs.h84
-rw-r--r--src/nvim/event/process.c6
-rw-r--r--src/nvim/event/time.c5
-rw-r--r--src/nvim/event/time.h1
-rw-r--r--src/nvim/ex_cmds.c358
-rw-r--r--src/nvim/ex_cmds.lua14
-rw-r--r--src/nvim/ex_cmds2.c461
-rw-r--r--src/nvim/ex_docmd.c486
-rw-r--r--src/nvim/ex_docmd.h14
-rw-r--r--src/nvim/ex_eval.c107
-rw-r--r--src/nvim/ex_getln.c198
-rw-r--r--src/nvim/ex_getln.h12
-rw-r--r--src/nvim/farsi.c31
-rw-r--r--src/nvim/file_search.c85
-rw-r--r--src/nvim/fileio.c274
-rw-r--r--src/nvim/fold.c33
-rw-r--r--src/nvim/func_attr.h3
-rw-r--r--src/nvim/garray.c19
-rw-r--r--src/nvim/getchar.c216
-rw-r--r--src/nvim/globals.h36
-rw-r--r--src/nvim/hardcopy.c34
-rw-r--r--src/nvim/if_cscope.c140
-rw-r--r--src/nvim/indent.c2
-rw-r--r--src/nvim/indent_c.c139
-rw-r--r--src/nvim/keymap.c194
-rw-r--r--src/nvim/keymap.h46
-rw-r--r--src/nvim/lib/khash.h2
-rw-r--r--src/nvim/lib/kvec.h5
-rw-r--r--src/nvim/macros.h11
-rw-r--r--src/nvim/main.c38
-rw-r--r--src/nvim/map.c24
-rw-r--r--src/nvim/map.h4
-rw-r--r--src/nvim/mark.c1
-rw-r--r--src/nvim/mbyte.c47
-rw-r--r--src/nvim/memfile.c144
-rw-r--r--src/nvim/memline.c19
-rw-r--r--src/nvim/menu.c10
-rw-r--r--src/nvim/message.c326
-rw-r--r--src/nvim/message.h51
-rw-r--r--src/nvim/misc1.c550
-rw-r--r--src/nvim/misc1.h1
-rw-r--r--src/nvim/misc2.c10
-rw-r--r--src/nvim/mouse.c94
-rw-r--r--src/nvim/mouse.h6
-rw-r--r--src/nvim/move.c21
-rw-r--r--src/nvim/msgpack_rpc/channel.c2
-rw-r--r--src/nvim/msgpack_rpc/defs.h18
-rw-r--r--src/nvim/msgpack_rpc/helpers.c4
-rw-r--r--src/nvim/msgpack_rpc/remote_ui.h9
-rw-r--r--src/nvim/msgpack_rpc/server.c4
-rw-r--r--src/nvim/normal.c471
-rw-r--r--src/nvim/normal.h15
-rw-r--r--src/nvim/ops.c810
-rw-r--r--src/nvim/ops.h8
-rw-r--r--src/nvim/option.c462
-rw-r--r--src/nvim/option_defs.h129
-rw-r--r--src/nvim/options.lua141
-rw-r--r--src/nvim/os/env.c146
-rw-r--r--src/nvim/os/fs.c104
-rw-r--r--src/nvim/os/fs_defs.h6
-rw-r--r--src/nvim/os/input.c19
-rw-r--r--src/nvim/os/mem.c2
-rw-r--r--src/nvim/os/stdpaths.c15
-rw-r--r--src/nvim/os/win_defs.h5
-rw-r--r--src/nvim/os_unix.c27
-rw-r--r--src/nvim/os_unix.h6
-rw-r--r--src/nvim/path.c417
-rw-r--r--src/nvim/path.h4
-rw-r--r--src/nvim/po/CMakeLists.txt2
-rw-r--r--src/nvim/po/eo.po50
-rw-r--r--src/nvim/po/es.po3
-rw-r--r--src/nvim/po/fr.po160
-rw-r--r--src/nvim/po/it.po502
-rw-r--r--src/nvim/popupmnu.c8
-rw-r--r--src/nvim/pos.h6
-rw-r--r--src/nvim/quickfix.c370
-rw-r--r--src/nvim/regexp.c25
-rw-r--r--src/nvim/regexp_nfa.c509
-rw-r--r--src/nvim/screen.c320
-rw-r--r--src/nvim/search.c269
-rw-r--r--src/nvim/search.h27
-rw-r--r--src/nvim/shada.c148
-rw-r--r--src/nvim/spell.c84
-rw-r--r--src/nvim/syntax.c230
-rw-r--r--src/nvim/syntax_defs.h4
-rw-r--r--src/nvim/tag.c61
-rw-r--r--src/nvim/tempfile.c138
-rw-r--r--src/nvim/tempfile.h8
-rw-r--r--src/nvim/terminal.c74
-rw-r--r--src/nvim/testdir/Makefile61
-rw-r--r--src/nvim/testdir/runtest.vim31
-rw-r--r--src/nvim/testdir/test1.in34
-rw-r--r--src/nvim/testdir/test10.in3
-rw-r--r--src/nvim/testdir/test10a.in1
-rw-r--r--src/nvim/testdir/test11.in84
-rw-r--r--src/nvim/testdir/test11.ok61
-rw-r--r--src/nvim/testdir/test12.in1
-rw-r--r--src/nvim/testdir/test13.in1
-rw-r--r--src/nvim/testdir/test14.in1
-rw-r--r--src/nvim/testdir/test17.in27
-rw-r--r--src/nvim/testdir/test30.in1
-rw-r--r--src/nvim/testdir/test32.in1
-rw-r--r--src/nvim/testdir/test34.in1
-rw-r--r--src/nvim/testdir/test36.in105
-rw-r--r--src/nvim/testdir/test36.ok96
-rw-r--r--src/nvim/testdir/test37.in2
-rw-r--r--src/nvim/testdir/test40.in1
-rw-r--r--src/nvim/testdir/test42.inbin2368 -> 2354 bytes
-rw-r--r--src/nvim/testdir/test47.in46
-rw-r--r--src/nvim/testdir/test47.ok40
-rw-r--r--src/nvim/testdir/test48.in1
-rw-r--r--src/nvim/testdir/test49.in5
-rw-r--r--src/nvim/testdir/test49.ok15
-rw-r--r--src/nvim/testdir/test49.vim875
-rw-r--r--src/nvim/testdir/test50.in1
-rw-r--r--src/nvim/testdir/test52.in1
-rw-r--r--src/nvim/testdir/test53.in1
-rw-r--r--src/nvim/testdir/test55.in12
-rw-r--r--src/nvim/testdir/test55.ok3
-rw-r--r--src/nvim/testdir/test64.in1
-rw-r--r--src/nvim/testdir/test68.in131
-rw-r--r--src/nvim/testdir/test68.ok77
-rw-r--r--src/nvim/testdir/test69.in2
-rw-r--r--src/nvim/testdir/test73.in1
-rw-r--r--src/nvim/testdir/test8.in3
-rw-r--r--src/nvim/testdir/test88.in99
-rw-r--r--src/nvim/testdir/test88.ok29
-rw-r--r--src/nvim/testdir/test_alot.vim3
-rw-r--r--src/nvim/testdir/test_breakindent.in123
-rw-r--r--src/nvim/testdir/test_breakindent.ok74
-rw-r--r--src/nvim/testdir/test_charsearch.in25
-rw-r--r--src/nvim/testdir/test_charsearch.ok3
-rw-r--r--src/nvim/testdir/test_close_count.in156
-rw-r--r--src/nvim/testdir/test_close_count.ok23
-rw-r--r--src/nvim/testdir/test_command_count.in158
-rw-r--r--src/nvim/testdir/test_command_count.ok38
-rw-r--r--src/nvim/testdir/test_cursor_func.vim52
-rw-r--r--src/nvim/testdir/test_help_tagjump.vim40
-rw-r--r--src/nvim/testdir/test_listlbr.in81
-rw-r--r--src/nvim/testdir/test_listlbr.ok43
-rw-r--r--src/nvim/testdir/test_marks.in34
-rw-r--r--src/nvim/testdir/test_marks.ok16
-rw-r--r--src/nvim/testdir/test_menu.vim9
-rw-r--r--src/nvim/testdir/test_timers.vim32
-rw-r--r--src/nvim/testdir/test_viml.vim969
-rw-r--r--src/nvim/tui/input.c12
-rw-r--r--src/nvim/tui/tui.c16
-rw-r--r--src/nvim/ugrid.h2
-rw-r--r--src/nvim/ui.c32
-rw-r--r--src/nvim/ui.h3
-rw-r--r--src/nvim/ui_bridge.c15
-rw-r--r--src/nvim/undo.c111
-rw-r--r--src/nvim/version.c852
-rw-r--r--src/nvim/vim.h40
-rw-r--r--src/nvim/window.c102
182 files changed, 13926 insertions, 10408 deletions
diff --git a/src/.clang-format b/src/.clang-format
index 35e545ac4b..5a910ff34b 100644
--- a/src/.clang-format
+++ b/src/.clang-format
@@ -1,4 +1,4 @@
-BasedOnStyle: llvm
+BasedOnStyle: Google
Language: Cpp
ColumnLimit: 80
IndentWidth: 2
@@ -10,3 +10,9 @@ AlignEscapedNewlinesLeft: false
AllowShortFunctionsOnASingleLine: false
SpacesBeforeTrailingComments: 2
PenaltyReturnTypeOnItsOwnLine: 200
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+BinPackParameters: false
+BreakBeforeBinaryOperators: true
+ContinuationIndentWidth: 4
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index ea5125e4e7..ab6f69f66c 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -1,4 +1,5 @@
include(CheckLibraryExists)
+include(CheckCCompilerFlag)
option(USE_GCOV "Enable gcov support" OFF)
@@ -41,25 +42,37 @@ include_directories(${GENERATED_DIR})
include_directories(${GENERATED_INCLUDES_DIR})
file(MAKE_DIRECTORY ${GENERATED_DIR})
-file(MAKE_DIRECTORY ${GENERATED_DIR}/os)
-file(MAKE_DIRECTORY ${GENERATED_DIR}/api)
-file(MAKE_DIRECTORY ${GENERATED_DIR}/api/private)
-file(MAKE_DIRECTORY ${GENERATED_DIR}/msgpack_rpc)
-file(MAKE_DIRECTORY ${GENERATED_DIR}/tui)
-file(MAKE_DIRECTORY ${GENERATED_DIR}/event)
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR})
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/os)
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api)
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api/private)
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/msgpack_rpc)
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/tui)
-file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/event)
-
-file(GLOB NEOVIM_SOURCES *.c os/*.c api/*.c api/private/*.c msgpack_rpc/*.c
- tui/*.c event/*.c)
+
+file(GLOB NEOVIM_SOURCES *.c)
+
+foreach(subdir
+ os
+ api
+ api/private
+ msgpack_rpc
+ tui
+ event
+ eval
+ )
+ if(${subdir} MATCHES "tui" AND NOT FEAT_TUI)
+ continue()
+ endif()
+
+ file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir})
+ file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir})
+ file(GLOB sources ${subdir}/*.c)
+ list(APPEND NEOVIM_SOURCES ${sources})
+endforeach()
+
file(GLOB_RECURSE NEOVIM_HEADERS *.h)
file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c)
+# Sort file lists to ensure generated files are created in the same order from
+# build to build.
+list(SORT NEOVIM_SOURCES)
+list(SORT NEOVIM_HEADERS)
+
foreach(sfile ${NEOVIM_SOURCES})
get_filename_component(f ${sfile} NAME)
if(${f} MATCHES "^(regexp_nfa.c)$")
@@ -72,23 +85,17 @@ list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove})
# Handle legacy files that don't yet pass -Wconversion.
set(CONV_SOURCES
buffer.c
- charset.c
diff.c
edit.c
eval.c
- ex_cmds2.c
ex_cmds.c
ex_docmd.c
ex_getln.c
fileio.c
- getchar.c
mbyte.c
memline.c
message.c
- misc1.c
ops.c
- path.c
- quickfix.c
regexp.c
screen.c
search.c
@@ -228,7 +235,6 @@ endif()
list(APPEND NVIM_LINK_LIBRARIES
${LIBUV_LIBRARIES}
${MSGPACK_LIBRARIES}
- ${LUAJIT_LIBRARIES}
${LIBVTERM_LIBRARIES}
${LIBTERMKEY_LIBRARIES}
${UNIBILIUM_LIBRARIES}
@@ -256,8 +262,14 @@ install_helper(TARGETS nvim)
if(CLANG_ASAN_UBSAN)
message(STATUS "Enabling Clang address sanitizer and undefined behavior sanitizer for nvim.")
+ check_c_compiler_flag(-fno-sanitize-recover=all SANITIZE_RECOVER_ALL)
+ if(SANITIZE_RECOVER_ALL)
+ set(SANITIZE_RECOVER -fno-sanitize-recover=all) # Clang 3.6+
+ else()
+ set(SANITIZE_RECOVER -fno-sanitize-recover) # Clang 3.5-
+ endif()
set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ")
- set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-fno-sanitize-recover -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/.asan-blacklist")
+ set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/.asan-blacklist")
set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=address -fsanitize=undefined ")
elseif(CLANG_MSAN)
message(STATUS "Enabling Clang memory sanitizer for nvim.")
diff --git a/src/nvim/README.md b/src/nvim/README.md
index e4939d94fd..f16c6de12f 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -11,7 +11,7 @@ that are constantly changing. As the code becomes more organized and stable,
this document will be updated to reflect the changes.
If you are looking for module-specific details, it is best to read the source
-code. Some files are extensively commented at the top(eg: terminal.c,
+code. Some files are extensively commented at the top (e.g. terminal.c,
screen.c).
### Top-level program loops
@@ -43,13 +43,13 @@ a typical editing session:
Note that we have split user actions into sequences of inputs that change the
state of the editor. While there's no documentation about a "g command
-mode"(step 16), internally it is implemented similarly to "operator-pending
+mode" (step 16), internally it is implemented similarly to "operator-pending
mode".
From this we can see that Vim has the behavior of a input-driven state
-machine(more specifically, a pushdown automaton since it requires a stack for
+machine (more specifically, a pushdown automaton since it requires a stack for
transitioning back from states). Assuming each state has a callback responsible
-for handling keys, this pseudocode(a python-like language) shows a good
+for handling keys, this pseudocode (a python-like language) shows a good
representation of the main program loop:
```py
@@ -129,20 +129,20 @@ def insert_state(data, key):
While the actual code is much more complicated, the above gives an idea of how
Neovim is organized internally. Some states like the `g_command_state` or
`get_operator_count_state` do not have a dedicated `state_enter` callback, but
-are implicitly embedded into other states(this will change later as we continue
+are implicitly embedded into other states (this will change later as we continue
the refactoring effort). To start reading the actual code, here's the
recommended order:
-1. `state_enter()` function(state.c). This is the actual program loop,
+1. `state_enter()` function (state.c). This is the actual program loop,
note that a `VimState` structure is used, which contains function pointers
for the callback and state data.
-2. `main()` function(main.c). After all startup, `normal_enter` is called
+2. `main()` function (main.c). After all startup, `normal_enter` is called
at the end of function to enter normal mode.
-3. `normal_enter()` function(normal.c) is a small wrapper for setting
+3. `normal_enter()` function (normal.c) is a small wrapper for setting
up the NormalState structure and calling `state_enter`.
-4. `normal_check()` function(normal.c) is called before each iteration of
+4. `normal_check()` function (normal.c) is called before each iteration of
normal mode.
-5. `normal_execute()` function(normal.c) is called when a key is read in normal
+5. `normal_execute()` function (normal.c) is called when a key is read in normal
mode.
The basic structure described for normal mode in 3, 4 and 5 is used for other
@@ -159,7 +159,7 @@ asynchronous events, which can include:
- msgpack-rpc requests
- job control callbacks
-- timers(not implemented yet but the support code is already there)
+- timers (not implemented yet but the support code is already there)
Neovim implements this functionality by entering another event loop while
waiting for characters, so instead of:
@@ -180,11 +180,11 @@ def state_enter(state_callback, data):
while state_callback(data, event) # invoke the callback for the current state
```
-where `event` is something the operating system delivers to us, including(but
+where `event` is something the operating system delivers to us, including (but
not limited to) user input. The `read_next_event()` part is internally
implemented by libuv, the platform layer used by Neovim.
Since Neovim inherited its code from Vim, the states are not prepared to receive
-"arbitrary events", so we use a special key to represent those(When a state
+"arbitrary events", so we use a special key to represent those (When a state
receives an "arbitrary event", it normally doesn't do anything other update the
screen).
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index b7a86af134..55b535c78c 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -19,6 +19,7 @@
#include "nvim/mark.h"
#include "nvim/fileio.h"
#include "nvim/move.h"
+#include "nvim/syntax.h"
#include "nvim/window.h"
#include "nvim/undo.h"
@@ -44,14 +45,22 @@ Integer buffer_line_count(Buffer buffer, Error *err)
/// Gets a buffer line
///
+/// @deprecated use buffer_get_lines instead.
+/// for positive indices (including 0) use
+/// "buffer_get_lines(buffer, index, index+1, true)"
+/// for negative indices use
+/// "buffer_get_lines(buffer, index-1, index, true)"
+///
/// @param buffer The buffer handle
/// @param index The line index
/// @param[out] err Details of an error that may have occurred
/// @return The line string
String buffer_get_line(Buffer buffer, Integer index, Error *err)
{
- String rv = {.size = 0};
- Array slice = buffer_get_line_slice(buffer, index, index, true, true, err);
+ String rv = { .size = 0 };
+
+ index = convert_index(index);
+ Array slice = buffer_get_lines(buffer, index, index+1, true, err);
if (!err->set && slice.size) {
rv = slice.items[0].data.string;
@@ -64,6 +73,12 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
/// Sets a buffer line
///
+/// @deprecated use buffer_set_lines instead.
+/// for positive indices use
+/// "buffer_set_lines(buffer, index, index+1, true, [line])"
+/// for negative indices use
+/// "buffer_set_lines(buffer, index-1, index, true, [line])"
+///
/// @param buffer The buffer handle
/// @param index The line index
/// @param line The new line.
@@ -71,23 +86,34 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
void buffer_set_line(Buffer buffer, Integer index, String line, Error *err)
{
Object l = STRING_OBJ(line);
- Array array = {.items = &l, .size = 1};
- buffer_set_line_slice(buffer, index, index, true, true, array, err);
+ Array array = { .items = &l, .size = 1 };
+ index = convert_index(index);
+ buffer_set_lines(buffer, index, index+1, true, array, err);
}
/// Deletes a buffer line
///
+/// @deprecated use buffer_set_lines instead.
+/// for positive indices use
+/// "buffer_set_lines(buffer, index, index+1, true, [])"
+/// for negative indices use
+/// "buffer_set_lines(buffer, index-1, index, true, [])"
/// @param buffer The buffer handle
/// @param index The line index
/// @param[out] err Details of an error that may have occurred
void buffer_del_line(Buffer buffer, Integer index, Error *err)
{
Array array = ARRAY_DICT_INIT;
- buffer_set_line_slice(buffer, index, index, true, true, array, err);
+ index = convert_index(index);
+ buffer_set_lines(buffer, index, index+1, true, array, err);
}
/// Retrieves a line range from the buffer
///
+/// @deprecated use buffer_get_lines(buffer, newstart, newend, false)
+/// where newstart = start + int(not include_start) - int(start < 0)
+/// newend = end + int(include_end) - int(end < 0)
+/// int(bool) = 1 if bool is true else 0
/// @param buffer The buffer handle
/// @param start The first line index
/// @param end The last line index
@@ -102,16 +128,48 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer,
Boolean include_end,
Error *err)
{
+ start = convert_index(start) + !include_start;
+ end = convert_index(end) + include_end;
+ return buffer_get_lines(buffer, start , end, false, err);
+}
+
+
+/// Retrieves a line range from the buffer
+///
+/// Indexing is zero-based, end-exclusive. Negative indices are interpreted
+/// as length+1+index, i e -1 refers to the index past the end. So to get the
+/// last element set start=-2 and end=-1.
+///
+/// Out-of-bounds indices are clamped to the nearest valid value, unless
+/// `strict_indexing` is set.
+///
+/// @param buffer The buffer handle
+/// @param start The first line index
+/// @param end The last line index (exclusive)
+/// @param strict_indexing whether out-of-bounds should be an error.
+/// @param[out] err Details of an error that may have occurred
+/// @return An array of lines
+ArrayOf(String) buffer_get_lines(Buffer buffer,
+ Integer start,
+ Integer end,
+ Boolean strict_indexing,
+ Error *err)
+{
Array rv = ARRAY_DICT_INIT;
buf_T *buf = find_buffer_by_handle(buffer, err);
- if (!buf || !inbounds(buf, start)) {
+ if (!buf) {
return rv;
}
- start = normalize_index(buf, start) + (include_start ? 0 : 1);
- include_end = include_end || (end >= buf->b_ml.ml_line_count);
- end = normalize_index(buf, end) + (include_end ? 1 : 0);
+ bool oob = false;
+ start = normalize_index(buf, start, &oob);
+ end = normalize_index(buf, end, &oob);
+
+ if (strict_indexing && oob) {
+ api_set_error(err, Validation, _("Index out of bounds"));
+ return rv;
+ }
if (start >= end) {
// Return 0-length array
@@ -151,8 +209,14 @@ end:
return rv;
}
+
/// Replaces a line range on the buffer
///
+/// @deprecated use buffer_set_lines(buffer, newstart, newend, false, lines)
+/// where newstart = start + int(not include_start) + int(start < 0)
+/// newend = end + int(include_end) + int(end < 0)
+/// int(bool) = 1 if bool is true else 0
+///
/// @param buffer The buffer handle
/// @param start The first line index
/// @param end The last line index
@@ -169,20 +233,52 @@ void buffer_set_line_slice(Buffer buffer,
ArrayOf(String) replacement,
Error *err)
{
+ start = convert_index(start) + !include_start;
+ end = convert_index(end) + include_end;
+ buffer_set_lines(buffer, start, end, false, replacement, err);
+}
+
+
+/// Replaces line range on the buffer
+///
+/// Indexing is zero-based, end-exclusive. Negative indices are interpreted
+/// as length+1+index, i e -1 refers to the index past the end. So to change
+/// or delete the last element set start=-2 and end=-1.
+///
+/// To insert lines at a given index, set both start and end to the same index.
+/// To delete a range of lines, set replacement to an empty array.
+///
+/// Out-of-bounds indices are clamped to the nearest valid value, unless
+/// `strict_indexing` is set.
+///
+/// @param buffer The buffer handle
+/// @param start The first line index
+/// @param end The last line index (exclusive)
+/// @param strict_indexing whether out-of-bounds should be an error.
+/// @param replacement An array of lines to use as replacement
+/// @param[out] err Details of an error that may have occurred
+void buffer_set_lines(Buffer buffer,
+ Integer start,
+ Integer end,
+ Boolean strict_indexing,
+ ArrayOf(String) replacement,
+ Error *err)
+{
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
return;
}
- if (!inbounds(buf, start)) {
+ bool oob = false;
+ start = normalize_index(buf, start, &oob);
+ end = normalize_index(buf, end, &oob);
+
+ if (strict_indexing && oob) {
api_set_error(err, Validation, _("Index out of bounds"));
return;
}
- start = normalize_index(buf, start) + (include_start ? 0 : 1);
- include_end = include_end || (end >= buf->b_ml.ml_line_count);
- end = normalize_index(buf, end) + (include_end ? 1 : 0);
if (start > end) {
api_set_error(err,
@@ -327,13 +423,16 @@ Object buffer_get_var(Buffer buffer, String name, Error *err)
return dict_get_value(buf->b_vars, name, err);
}
-/// Sets a buffer-scoped (b:) variable. 'nil' value deletes the variable.
+/// Sets a buffer-scoped (b:) variable
///
/// @param buffer The buffer handle
/// @param name The variable name
/// @param value The variable value
/// @param[out] err Details of an error that may have occurred
-/// @return The old value
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -342,7 +441,27 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
return (Object) OBJECT_INIT;
}
- return dict_set_value(buf->b_vars, name, value, err);
+ return dict_set_value(buf->b_vars, name, value, false, err);
+}
+
+/// Removes a buffer-scoped (b:) variable
+///
+/// @param buffer The buffer handle
+/// @param name The variable name
+/// @param[out] err Details of an error that may have occurred
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
+Object buffer_del_var(Buffer buffer, String name, Error *err)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+
+ if (!buf) {
+ return (Object) OBJECT_INIT;
+ }
+
+ return dict_set_value(buf->b_vars, name, NIL, true, err);
}
/// Gets a buffer option value
@@ -456,6 +575,8 @@ Boolean buffer_is_valid(Buffer buffer)
/// Inserts a sequence of lines to a buffer at a certain index
///
+/// @deprecated use buffer_set_lines(buffer, lnum, lnum, true, lines)
+///
/// @param buffer The buffer handle
/// @param lnum Insert the lines after `lnum`. If negative, it will append
/// to the end of the buffer.
@@ -466,8 +587,9 @@ void buffer_insert(Buffer buffer,
ArrayOf(String) lines,
Error *err)
{
- bool end_start = lnum < 0;
- buffer_set_line_slice(buffer, lnum, lnum, !end_start, end_start, lines, err);
+ // "lnum" will be the index of the line after inserting,
+ // no matter if it is negative or not
+ buffer_set_lines(buffer, lnum, lnum, true, lines, err);
}
/// Return a tuple (row,col) representing the position of the named mark
@@ -514,6 +636,99 @@ ArrayOf(Integer, 2) buffer_get_mark(Buffer buffer, String name, Error *err)
return rv;
}
+/// Adds a highlight to buffer.
+///
+/// This can be used for plugins which dynamically generate highlights to a
+/// buffer (like a semantic highlighter or linter). The function adds a single
+/// highlight to a buffer. Unlike matchaddpos() highlights follow changes to
+/// line numbering (as lines are inserted/removed above the highlighted line),
+/// like signs and marks do.
+///
+/// "src_id" is useful for batch deletion/updating of a set of highlights. When
+/// called with src_id = 0, an unique source id is generated and returned.
+/// Succesive calls can pass in it as "src_id" to add new highlights to the same
+/// source group. All highlights in the same group can then be cleared with
+/// buffer_clear_highlight. If the highlight never will be manually deleted
+/// pass in -1 for "src_id".
+///
+/// If "hl_group" is the empty string no highlight is added, but a new src_id
+/// is still returned. This is useful for an external plugin to synchrounously
+/// request an unique src_id at initialization, and later asynchronously add and
+/// clear highlights in response to buffer changes.
+///
+/// @param buffer The buffer handle
+/// @param src_id Source group to use or 0 to use a new group,
+/// or -1 for ungrouped highlight
+/// @param hl_group Name of the highlight group to use
+/// @param line The line to highlight
+/// @param col_start Start of range of columns to highlight
+/// @param col_end End of range of columns to highlight,
+/// or -1 to highlight to end of line
+/// @param[out] err Details of an error that may have occurred
+/// @return The src_id that was used
+Integer buffer_add_highlight(Buffer buffer,
+ Integer src_id,
+ String hl_group,
+ Integer line,
+ Integer col_start,
+ Integer col_end,
+ Error *err)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return 0;
+ }
+
+ if (line < 0 || line >= MAXLNUM) {
+ api_set_error(err, Validation, _("Line number outside range"));
+ return 0;
+ }
+ if (col_start < 0 || col_start > MAXCOL) {
+ api_set_error(err, Validation, _("Column value outside range"));
+ return 0;
+ }
+ if (col_end < 0 || col_end > MAXCOL) {
+ col_end = MAXCOL;
+ }
+
+ int hlg_id = syn_name2id((char_u*)hl_group.data);
+ src_id = bufhl_add_hl(buf, (int)src_id, hlg_id, (linenr_T)line+1,
+ (colnr_T)col_start+1, (colnr_T)col_end);
+ return src_id;
+}
+
+/// Clears highlights from a given source group and a range of lines
+///
+/// To clear a source group in the entire buffer, pass in 1 and -1 to
+/// line_start and line_end respectively.
+///
+/// @param buffer The buffer handle
+/// @param src_id Highlight source group to clear, or -1 to clear all groups.
+/// @param line_start Start of range of lines to clear
+/// @param line_end End of range of lines to clear (exclusive)
+/// or -1 to clear to end of file.
+/// @param[out] err Details of an error that may have occurred
+void buffer_clear_highlight(Buffer buffer,
+ Integer src_id,
+ Integer line_start,
+ Integer line_end,
+ Error *err)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return;
+ }
+
+ if (line_start < 0 || line_start >= MAXLNUM) {
+ api_set_error(err, Validation, _("Line number outside range"));
+ return;
+ }
+ if (line_end < 0 || line_end > MAXLNUM) {
+ line_end = MAXLNUM;
+ }
+
+ bufhl_clear_line_range(buf, (int)src_id, (int)line_start+1, (int)line_end);
+}
// Check if deleting lines made the cursor position invalid.
// Changed the lines from "lo" to "hi" and added "extra" lines (negative if
@@ -538,20 +753,26 @@ static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra)
}
// Normalizes 0-based indexes to buffer line numbers
-static int64_t normalize_index(buf_T *buf, int64_t index)
+static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob)
{
+ int64_t line_count = buf->b_ml.ml_line_count;
// Fix if < 0
- index = index < 0 ? buf->b_ml.ml_line_count + index : index;
+ index = index < 0 ? line_count + index +1 : index;
+
+ // Check for oob
+ if (index > line_count) {
+ *oob = true;
+ index = line_count;
+ } else if (index < 0) {
+ *oob = true;
+ index = 0;
+ }
// Convert the index to a vim line number
index++;
- // Fix if > line_count
- index = index > buf->b_ml.ml_line_count ? buf->b_ml.ml_line_count : index;
return index;
}
-// Returns true if the 0-indexed `index` is within the 1-indexed buffer bounds.
-static bool inbounds(buf_T *buf, int64_t index)
+static int64_t convert_index(int64_t index)
{
- linenr_T nlines = buf->b_ml.ml_line_count;
- return index >= -nlines && index < nlines;
+ return index < 0 ? index - 1 : index;
}
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index 6c8e324649..fbfa87d5ae 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -99,4 +99,3 @@ struct key_value_pair {
#endif // NVIM_API_PRIVATE_DEFS_H
-
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 7a0b5191d7..db3e499427 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -90,14 +90,17 @@ Object dict_get_value(dict_T *dict, String key, Error *err)
}
/// Set a value in a dict. Objects are recursively expanded into their
-/// vimscript equivalents. Passing 'nil' as value deletes the key.
+/// vimscript equivalents.
///
/// @param dict The vimscript dict
/// @param key The key
/// @param value The new value
+/// @param del Delete key in place of setting it. Argument `value` is ignored in
+/// this case.
/// @param[out] err Details of an error that may have occurred
/// @return the old value, if any
-Object dict_set_value(dict_T *dict, String key, Object value, Error *err)
+Object dict_set_value(dict_T *dict, String key, Object value, bool del,
+ Error *err)
{
Object rv = OBJECT_INIT;
@@ -118,7 +121,7 @@ Object dict_set_value(dict_T *dict, String key, Object value, Error *err)
dictitem_T *di = dict_find(dict, (uint8_t *)key.data, (int)key.size);
- if (value.type == kObjectTypeNil) {
+ if (del) {
// Delete the key
if (di == NULL) {
// Doesn't exist, fail
@@ -397,13 +400,13 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
switch (obj.type) {
case kObjectTypeNil:
- tv->v_type = VAR_NUMBER;
- tv->vval.v_number = 0;
+ tv->v_type = VAR_SPECIAL;
+ tv->vval.v_special = kSpecialVarNull;
break;
case kObjectTypeBoolean:
- tv->v_type = VAR_NUMBER;
- tv->vval.v_number = obj.data.boolean;
+ tv->v_type = VAR_SPECIAL;
+ tv->vval.v_special = obj.data.boolean? kSpecialVarTrue: kSpecialVarFalse;
break;
case kObjectTypeBuffer:
@@ -651,6 +654,21 @@ static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup)
}
switch (obj->v_type) {
+ case VAR_SPECIAL:
+ switch (obj->vval.v_special) {
+ case kSpecialVarTrue:
+ case kSpecialVarFalse: {
+ rv.type = kObjectTypeBoolean;
+ rv.data.boolean = (obj->vval.v_special == kSpecialVarTrue);
+ break;
+ }
+ case kSpecialVarNull: {
+ rv.type = kObjectTypeNil;
+ break;
+ }
+ }
+ break;
+
case VAR_STRING:
rv.type = kObjectTypeString;
rv.data.string = cstr_to_string((char *) obj->vval.v_string);
@@ -730,6 +748,10 @@ static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup)
}
}
break;
+
+ case VAR_UNKNOWN:
+ case VAR_FUNC:
+ break;
}
return rv;
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index 126ee4072d..c8311b0aa0 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -54,13 +54,16 @@ Object tabpage_get_var(Tabpage tabpage, String name, Error *err)
return dict_get_value(tab->tp_vars, name, err);
}
-/// Sets a tab-scoped (t:) variable. 'nil' value deletes the variable.
+/// Sets a tab-scoped (t:) variable
///
/// @param tabpage handle
/// @param name The variable name
/// @param value The variable value
/// @param[out] err Details of an error that may have occurred
-/// @return The tab page handle
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err)
{
tabpage_T *tab = find_tab_by_handle(tabpage, err);
@@ -69,7 +72,27 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err)
return (Object) OBJECT_INIT;
}
- return dict_set_value(tab->tp_vars, name, value, err);
+ return dict_set_value(tab->tp_vars, name, value, false, err);
+}
+
+/// Removes a tab-scoped (t:) variable
+///
+/// @param tabpage handle
+/// @param name The variable name
+/// @param[out] err Details of an error that may have occurred
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
+Object tabpage_del_var(Tabpage tabpage, String name, Error *err)
+{
+ tabpage_T *tab = find_tab_by_handle(tabpage, err);
+
+ if (!tab) {
+ return (Object) OBJECT_INIT;
+ }
+
+ return dict_set_value(tab->tp_vars, name, NIL, true, err);
}
/// Gets the current window in a tab page
diff --git a/src/nvim/msgpack_rpc/remote_ui.c b/src/nvim/api/ui.c
index f0d92b52a0..1703d49296 100644
--- a/src/nvim/msgpack_rpc/remote_ui.c
+++ b/src/nvim/api/ui.c
@@ -7,13 +7,12 @@
#include "nvim/ui.h"
#include "nvim/memory.h"
#include "nvim/map.h"
-#include "nvim/msgpack_rpc/remote_ui.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "msgpack_rpc/remote_ui.c.generated.h"
+# include "api/ui.c.generated.h"
#endif
typedef struct {
@@ -24,21 +23,13 @@ typedef struct {
static PMap(uint64_t) *connected_uis = NULL;
void remote_ui_init(void)
+ FUNC_API_NOEXPORT
{
connected_uis = pmap_new(uint64_t)();
- // Add handler for "attach_ui"
- String method = cstr_as_string("ui_attach");
- MsgpackRpcRequestHandler handler = {.fn = remote_ui_attach, .async = false};
- msgpack_rpc_add_method_handler(method, handler);
- method = cstr_as_string("ui_detach");
- handler.fn = remote_ui_detach;
- msgpack_rpc_add_method_handler(method, handler);
- method = cstr_as_string("ui_try_resize");
- handler.fn = remote_ui_try_resize;
- msgpack_rpc_add_method_handler(method, handler);
}
void remote_ui_disconnect(uint64_t channel_id)
+ FUNC_API_NOEXPORT
{
UI *ui = pmap_get(uint64_t)(connected_uis, channel_id);
if (!ui) {
@@ -49,34 +40,30 @@ void remote_ui_disconnect(uint64_t channel_id)
api_free_array(data->buffer);
pmap_del(uint64_t)(connected_uis, channel_id);
xfree(ui->data);
- ui_detach(ui);
+ ui_detach_impl(ui);
xfree(ui);
}
-static Object remote_ui_attach(uint64_t channel_id, uint64_t request_id,
- Array args, Error *error)
+void ui_attach(uint64_t channel_id, Integer width, Integer height,
+ Boolean enable_rgb, Error *err)
{
if (pmap_has(uint64_t)(connected_uis, channel_id)) {
- api_set_error(error, Exception, _("UI already attached for channel"));
- return NIL;
+ api_set_error(err, Exception, _("UI already attached for channel"));
+ return;
}
- if (args.size != 3 || args.items[0].type != kObjectTypeInteger
- || args.items[1].type != kObjectTypeInteger
- || args.items[2].type != kObjectTypeBoolean
- || args.items[0].data.integer <= 0 || args.items[1].data.integer <= 0) {
- api_set_error(error, Validation,
- _("Invalid arguments. Expected: "
- "(uint width > 0, uint height > 0, bool enable_rgb)"));
- return NIL;
+ if (width <= 0 || height <= 0) {
+ api_set_error(err, Validation,
+ _("Expected width > 0 and height > 0"));
+ return;
}
UIData *data = xmalloc(sizeof(UIData));
data->channel_id = channel_id;
data->buffer = (Array)ARRAY_DICT_INIT;
UI *ui = xcalloc(1, sizeof(UI));
- ui->width = (int)args.items[0].data.integer;
- ui->height = (int)args.items[1].data.integer;
- ui->rgb = args.items[2].data.boolean;
+ ui->width = (int)width;
+ ui->height = (int)height;
+ ui->rgb = enable_rgb;
ui->data = data;
ui->resize = remote_ui_resize;
ui->clear = remote_ui_clear;
@@ -96,50 +83,44 @@ static Object remote_ui_attach(uint64_t channel_id, uint64_t request_id,
ui->visual_bell = remote_ui_visual_bell;
ui->update_fg = remote_ui_update_fg;
ui->update_bg = remote_ui_update_bg;
+ ui->update_sp = remote_ui_update_sp;
ui->flush = remote_ui_flush;
ui->suspend = remote_ui_suspend;
ui->set_title = remote_ui_set_title;
ui->set_icon = remote_ui_set_icon;
pmap_put(uint64_t)(connected_uis, channel_id, ui);
- ui_attach(ui);
- return NIL;
+ ui_attach_impl(ui);
+ return;
}
-static Object remote_ui_detach(uint64_t channel_id, uint64_t request_id,
- Array args, Error *error)
+void ui_detach(uint64_t channel_id, Error *err)
{
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
- api_set_error(error, Exception, _("UI is not attached for channel"));
+ api_set_error(err, Exception, _("UI is not attached for channel"));
}
remote_ui_disconnect(channel_id);
-
- return NIL;
}
-static Object remote_ui_try_resize(uint64_t channel_id, uint64_t request_id,
- Array args, Error *error)
+Object ui_try_resize(uint64_t channel_id, Integer width,
+ Integer height, Error *err)
{
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
- api_set_error(error, Exception, _("UI is not attached for channel"));
+ api_set_error(err, Exception, _("UI is not attached for channel"));
}
- if (args.size != 2 || args.items[0].type != kObjectTypeInteger
- || args.items[1].type != kObjectTypeInteger
- || args.items[0].data.integer <= 0 || args.items[1].data.integer <= 0) {
- api_set_error(error, Validation,
- _("Invalid arguments. Expected: "
- "(uint width > 0, uint height > 0)"));
+ if (width <= 0 || height <= 0) {
+ api_set_error(err, Validation,
+ _("Expected width > 0 and height > 0"));
return NIL;
}
UI *ui = pmap_get(uint64_t)(connected_uis, channel_id);
- ui->width = (int)args.items[0].data.integer;
- ui->height = (int)args.items[1].data.integer;
+ ui->width = (int)width;
+ ui->height = (int)height;
ui_refresh();
return NIL;
}
-
static void push_call(UI *ui, char *name, Array args)
{
Array call = ARRAY_DICT_INIT;
@@ -235,7 +216,7 @@ static void remote_ui_mode_change(UI *ui, int mode)
}
static void remote_ui_set_scroll_region(UI *ui, int top, int bot, int left,
- int right)
+ int right)
{
Array args = ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(top));
@@ -285,6 +266,10 @@ static void remote_ui_highlight_set(UI *ui, HlAttrs attrs)
PUT(hl, "background", INTEGER_OBJ(attrs.background));
}
+ if (attrs.special != -1) {
+ PUT(hl, "special", INTEGER_OBJ(attrs.special));
+ }
+
ADD(args, DICTIONARY_OBJ(hl));
push_call(ui, "highlight_set", args);
}
@@ -292,7 +277,7 @@ static void remote_ui_highlight_set(UI *ui, HlAttrs attrs)
static void remote_ui_put(UI *ui, uint8_t *data, size_t size)
{
Array args = ARRAY_DICT_INIT;
- String str = {.data = xmemdupz(data, size), .size = size};
+ String str = { .data = xmemdupz(data, size), .size = size };
ADD(args, STRING_OBJ(str));
push_call(ui, "put", args);
}
@@ -323,6 +308,13 @@ static void remote_ui_update_bg(UI *ui, int bg)
push_call(ui, "update_bg", args);
}
+static void remote_ui_update_sp(UI *ui, int sp)
+{
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ(sp));
+ push_call(ui, "update_sp", args);
+}
+
static void remote_ui_flush(UI *ui)
{
UIData *data = ui->data;
diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h
new file mode 100644
index 0000000000..b3af14f8a8
--- /dev/null
+++ b/src/nvim/api/ui.h
@@ -0,0 +1,11 @@
+#ifndef NVIM_API_UI_H
+#define NVIM_API_UI_H
+
+#include <stdint.h>
+
+#include "nvim/api/private/defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/ui.h.generated.h"
+#endif
+#endif // NVIM_API_UI_H
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 9279f6b469..46d72b847d 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -98,7 +98,7 @@ void vim_feedkeys(String keys, String mode, Boolean escape_csi)
/// @return The number of bytes actually written, which can be lower than
/// requested if the buffer becomes full.
Integer vim_input(String keys)
- FUNC_ATTR_ASYNC
+ FUNC_API_ASYNC
{
return (Integer)input_enqueue(keys);
}
@@ -116,8 +116,14 @@ String vim_replace_termcodes(String str, Boolean from_part, Boolean do_lt,
}
char *ptr = NULL;
- replace_termcodes((char_u *)str.data, (char_u **)&ptr,
- from_part, do_lt, special);
+ // Set 'cpoptions' the way we want it.
+ // FLAG_CPO_BSLASH set - backslashes are *not* treated specially
+ // FLAG_CPO_KEYCODE set - keycodes are *not* reverse-engineered
+ // FLAG_CPO_SPECI unset - <Key> sequences *are* interpreted
+ // The third from end parameter of replace_termcodes() is true so that the
+ // <lt> sequence is recognised - needed for a real backslash.
+ replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr,
+ from_part, do_lt, special, CPO_TO_CPO_FLAGS);
return cstr_as_string(ptr);
}
@@ -291,7 +297,7 @@ void vim_change_directory(String dir, Error *err)
return;
}
- post_chdir(false);
+ post_chdir(kCdScopeGlobal);
try_end(err);
}
@@ -331,15 +337,31 @@ Object vim_get_var(String name, Error *err)
return dict_get_value(&globvardict, name, err);
}
-/// Sets a global variable. Passing 'nil' as value deletes the variable.
+/// Sets a global variable
///
/// @param name The variable name
/// @param value The variable value
/// @param[out] err Details of an error that may have occurred
-/// @return the old value if any
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
Object vim_set_var(String name, Object value, Error *err)
{
- return dict_set_value(&globvardict, name, value, err);
+ return dict_set_value(&globvardict, name, value, false, err);
+}
+
+/// Removes a global variable
+///
+/// @param name The variable name
+/// @param[out] err Details of an error that may have occurred
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
+Object vim_del_var(String name, Error *err)
+{
+ return dict_set_value(&globvardict, name, NIL, true, err);
}
/// Gets a vim variable
@@ -596,7 +618,7 @@ Dictionary vim_get_color_map(void)
Array vim_get_api_info(uint64_t channel_id)
- FUNC_ATTR_ASYNC
+ FUNC_API_ASYNC
{
Array rv = ARRAY_DICT_INIT;
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index aad616c7bf..f644453358 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -57,8 +57,8 @@ void window_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
{
win_T *win = find_window_by_handle(window, err);
- if (pos.size != 2 || pos.items[0].type != kObjectTypeInteger ||
- pos.items[1].type != kObjectTypeInteger) {
+ if (pos.size != 2 || pos.items[0].type != kObjectTypeInteger
+ || pos.items[1].type != kObjectTypeInteger) {
api_set_error(err,
Validation,
_("Argument \"pos\" must be a [row, col] array"));
@@ -197,13 +197,16 @@ Object window_get_var(Window window, String name, Error *err)
return dict_get_value(win->w_vars, name, err);
}
-/// Sets a window-scoped (w:) variable. 'nil' value deletes the variable.
+/// Sets a window-scoped (w:) variable
///
/// @param window The window handle
/// @param name The variable name
/// @param value The variable value
/// @param[out] err Details of an error that may have occurred
-/// @return The old value
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
Object window_set_var(Window window, String name, Object value, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -212,7 +215,27 @@ Object window_set_var(Window window, String name, Object value, Error *err)
return (Object) OBJECT_INIT;
}
- return dict_set_value(win->w_vars, name, value, err);
+ return dict_set_value(win->w_vars, name, value, false, err);
+}
+
+/// Removes a window-scoped (w:) variable
+///
+/// @param window The window handle
+/// @param name The variable name
+/// @param[out] err Details of an error that may have occurred
+/// @return The old value or nil if there was no previous value.
+///
+/// @warning It may return nil if there was no previous value
+/// or if previous value was `v:null`.
+Object window_del_var(Window window, String name, Error *err)
+{
+ win_T *win = find_window_by_handle(window, err);
+
+ if (!win) {
+ return (Object) OBJECT_INIT;
+ }
+
+ return dict_set_value(win->w_vars, name, NIL, true, err);
}
/// Gets a window option value
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index b432e12c02..db97bd9dc4 100644
--- a/src/nvim/arabic.c
+++ b/src/nvim/arabic.c
@@ -1367,8 +1367,8 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1,
int prev_laa = A_firstc_laa(prev_c, prev_c1);
if (curr_laa) {
- if (A_is_valid(prev_c) && !A_is_f(shape_c) && !A_is_s(shape_c) &&
- !prev_laa) {
+ if (A_is_valid(prev_c) && !A_is_f(shape_c) && !A_is_s(shape_c)
+ && !prev_laa) {
curr_c = chg_c_laa2f(curr_laa);
} else {
curr_c = chg_c_laa2i(curr_laa);
@@ -1454,19 +1454,19 @@ static bool A_is_harakat(int c)
// (alphabet/number/punctuation)
static bool A_is_iso(int c)
{
- return (c >= a_HAMZA && c <= a_GHAIN) ||
- (c >= a_TATWEEL && c <= a_HAMZA_BELOW) ||
- c == a_MINI_ALEF;
+ return ((c >= a_HAMZA && c <= a_GHAIN)
+ || (c >= a_TATWEEL && c <= a_HAMZA_BELOW)
+ || c == a_MINI_ALEF);
}
// A_is_formb returns true if 'c' is an Arabic 10646-1 FormB character.
// (alphabet/number/punctuation)
static bool A_is_formb(int c)
{
- return (c >= a_s_FATHATAN && c <= a_s_DAMMATAN) ||
- c == a_s_KASRATAN ||
- (c >= a_s_FATHA && c <= a_f_LAM_ALEF) ||
- c == a_BYTE_ORDER_MARK;
+ return ((c >= a_s_FATHATAN && c <= a_s_DAMMATAN)
+ || c == a_s_KASRATAN
+ || (c >= a_s_FATHA && c <= a_f_LAM_ALEF)
+ || c == a_BYTE_ORDER_MARK);
}
// A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B).
diff --git a/src/nvim/assert.h b/src/nvim/assert.h
index 3a900aca65..761636305e 100644
--- a/src/nvim/assert.h
+++ b/src/nvim/assert.h
@@ -8,17 +8,32 @@
// defined(__has_feature) && __has_feature(...). Therefore we define Clang's
// __has_feature and __has_extension macro's before referring to them.
#ifndef __has_feature
- #define __has_feature(x) 0
+# define __has_feature(x) 0
#endif
#ifndef __has_extension
- #define __has_extension __has_feature
+# define __has_extension __has_feature
#endif
-/// STATIC_ASSERT(condition, message) - assert at compile time if !cond
+/// @def STATIC_ASSERT
+/// @brief Assert at compile time if condition is not satisfied.
///
-/// example:
-/// STATIC_ASSERT(sizeof(void *) == 8, "need 64-bits mode");
+/// Should be put on its own line, followed by a semicolon.
+///
+/// Example:
+///
+/// STATIC_ASSERT(sizeof(void *) == 8, "Expected 64-bit mode");
+///
+/// @param[in] condition Condition to check, should be an integer constant
+/// expression.
+/// @param[in] message Message which will be given if check fails.
+
+/// @def STATIC_ASSERT_EXPR
+/// @brief Like #STATIC_ASSERT, but can be used where expressions are used.
+///
+/// STATIC_ASSERT_EXPR may be put in brace initializer lists. Error message
+/// given in this case is not very nice with the current implementation though
+/// and `message` argument is ignored.
// define STATIC_ASSERT as C11's _Static_assert whenever either C11 mode is
// detected or the compiler is known to support it. Note that Clang in C99
@@ -29,50 +44,81 @@
// clearer messages we get from _Static_assert, we suppress the warnings
// temporarily.
+#define STATIC_ASSERT_PRAGMA_START
+#define STATIC_ASSERT_PRAGMA_END
+#define STATIC_ASSERT(cond, msg) \
+ do { \
+ STATIC_ASSERT_PRAGMA_START \
+ STATIC_ASSERT_STATEMENT(cond, msg); \
+ STATIC_ASSERT_PRAGMA_END \
+ } while (0)
+
// the easiest case, when the mode is C11 (generic compiler) or Clang
// advertises explicit support for c_static_assert, meaning it won't warn.
#if __STDC_VERSION__ >= 201112L || __has_feature(c_static_assert)
- #define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
+# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
// if we're dealing with gcc >= 4.6 in C99 mode, we can still use
// _Static_assert but we need to suppress warnings, this is pretty ugly.
#elif (!defined(__clang__) && !defined(__INTEL_COMPILER)) && \
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
- #define STATIC_ASSERT(cond, msg) \
- _Pragma("GCC diagnostic push") \
- _Pragma("GCC diagnostic ignored \"-pedantic\"") \
- _Static_assert(cond, msg); \
- _Pragma("GCC diagnostic pop") \
+
+# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
+
+# undef STATIC_ASSERT_PRAGMA_START
+
+#if __GNUC__ >= 6
+# define STATIC_ASSERT_PRAGMA_START \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wpedantic\"")
+#else
+# define STATIC_ASSERT_PRAGMA_START \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-pedantic\"")
+#endif
+
+# undef STATIC_ASSERT_PRAGMA_END
+# define STATIC_ASSERT_PRAGMA_END \
+ _Pragma("GCC diagnostic pop") \
// the same goes for clang in C99 mode, but we suppress a different warning
#elif defined(__clang__) && __has_extension(c_static_assert)
- #define STATIC_ASSERT(cond, msg) \
- _Pragma("clang diagnostic push") \
- _Pragma("clang diagnostic ignored \"-Wc11-extensions\"") \
- _Static_assert(cond, msg); \
- _Pragma("clang diagnostic pop") \
+
+# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
+
+# undef STATIC_ASSERT_PRAGMA_START
+# define STATIC_ASSERT_PRAGMA_START \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wc11-extensions\"") \
+
+# undef STATIC_ASSERT_PRAGMA_END
+# define STATIC_ASSERT_PRAGMA_END \
+ _Pragma("clang diagnostic pop") \
// TODO(aktau): verify that this works, don't have MSVC on hand.
#elif _MSC_VER >= 1600
- #define STATIC_ASSERT(cond, msg) static_assert(cond, msg)
+
+# define STATIC_ASSERT_STATEMENT(cond, msg) static_assert(cond, msg)
// fallback for compilers that don't support _Static_assert or static_assert
// not as pretty but gets the job done. Credit goes to Pádraig Brady and
// contributors.
#else
- #define ASSERT_CONCAT_(a, b) a##b
- #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
- // These can't be used after statements in c89.
- #ifdef __COUNTER__
- #define STATIC_ASSERT(e,m) \
- { enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) }; }
- #else
- // This can't be used twice on the same line so ensure if using in headers
- // that the headers are not included twice (by wrapping in #ifndef...#endif)
- // Note it doesn't cause an issue when used on same line of separate modules
- // compiled with gcc -combine -fwhole-program.
- #define STATIC_ASSERT(e,m) \
- { enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }; }
- #endif
+# define STATIC_ASSERT_STATEMENT STATIC_ASSERT_EXPR
+#endif
+
+#define ASSERT_CONCAT_(a, b) a##b
+#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
+// These can't be used after statements in c89.
+#ifdef __COUNTER__
+# define STATIC_ASSERT_EXPR(e, m) \
+ ((enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) }) 0)
+#else
+// This can't be used twice on the same line so ensure if using in headers
+// that the headers are not included twice (by wrapping in #ifndef...#endif)
+// Note it doesn't cause an issue when used on same line of separate modules
+// compiled with gcc -combine -fwhole-program.
+# define STATIC_ASSERT_EXPR(e, m) \
+ ((enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }) 0)
#endif
#endif // NVIM_ASSERT_H
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index aa4a8d8332..8d891effae 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -83,6 +83,7 @@ return {
'TermResponse', -- after setting "v:termresponse"
'TextChanged', -- text was modified
'TextChangedI', -- text was modified in Insert mode
+ 'TextYankPost', -- after a yank or delete was done (y, d, c)
'User', -- user defined autocommand
'VimEnter', -- after starting Vim
'VimLeave', -- before exiting Vim
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 34e24712cd..72716daf0e 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -141,14 +141,21 @@ open_buffer (
/* mark cursor position as being invalid */
curwin->w_valid = 0;
- if (curbuf->b_ffname != NULL
- ) {
+ if (curbuf->b_ffname != NULL) {
+ int old_msg_silent = msg_silent;
+ if (shortmess(SHM_FILEINFO)) {
+ msg_silent = 1;
+ }
+
retval = readfile(curbuf->b_ffname, curbuf->b_fname,
- (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap,
- flags | READ_NEW);
- /* Help buffer is filtered. */
- if (curbuf->b_help)
+ (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap,
+ flags | READ_NEW);
+ msg_silent = old_msg_silent;
+
+ // Help buffer is filtered.
+ if (curbuf->b_help) {
fix_help_buffer();
+ }
} else if (read_stdin) {
int save_bin = curbuf->b_p_bin;
linenr_T line_count;
@@ -257,17 +264,16 @@ open_buffer (
return retval;
}
-/*
- * Return TRUE if "buf" points to a valid buffer (in the buffer list).
- */
-int buf_valid(buf_T *buf)
+/// Check that "buf" points to a valid buffer (in the buffer list).
+bool buf_valid(buf_T *buf)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
FOR_ALL_BUFFERS(bp) {
if (bp == buf) {
- return TRUE;
+ return true;
}
}
- return FALSE;
+ return false;
}
/*
@@ -401,10 +407,9 @@ close_buffer (
buf->b_nwindows = nwindows;
buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0));
- if (
- win_valid(win) &&
- win->w_buffer == buf)
- win->w_buffer = NULL; /* make sure we don't use the buffer now */
+ if (win_valid(win) && win->w_buffer == buf) {
+ win->w_buffer = NULL; // make sure we don't use the buffer now
+ }
/* Autocommands may have deleted the buffer. */
if (!buf_valid(buf))
@@ -580,16 +585,17 @@ free_buffer_stuff (
)
{
if (free_options) {
- clear_wininfo(buf); /* including window-local options */
- free_buf_options(buf, TRUE);
+ clear_wininfo(buf); // including window-local options
+ free_buf_options(buf, true);
ga_clear(&buf->b_s.b_langp);
}
- vars_clear(&buf->b_vars->dv_hashtab); /* free all internal variables */
+ vars_clear(&buf->b_vars->dv_hashtab); // free all internal variables
hash_init(&buf->b_vars->dv_hashtab);
- uc_clear(&buf->b_ucmds); /* clear local user commands */
- buf_delete_signs(buf); /* delete any signs */
- map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */
- map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */
+ uc_clear(&buf->b_ucmds); // clear local user commands
+ buf_delete_signs(buf); // delete any signs
+ bufhl_clear_all(buf); // delete any highligts
+ map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings
+ map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
xfree(buf->b_start_fenc);
buf->b_start_fenc = NULL;
}
@@ -1287,14 +1293,15 @@ void enter_buffer(buf_T *buf)
redraw_later(NOT_VALID);
}
-/*
- * Change to the directory of the current buffer.
- */
+// Change to the directory of the current buffer.
+// Don't do this while still starting up.
void do_autochdir(void)
{
if (p_acd) {
- if (curbuf->b_ffname != NULL && vim_chdirfile(curbuf->b_ffname) == OK) {
- shorten_fnames(TRUE);
+ if (starting == 0
+ && curbuf->b_ffname != NULL
+ && vim_chdirfile(curbuf->b_ffname) == OK) {
+ shorten_fnames(true);
}
}
}
@@ -1332,8 +1339,8 @@ buflist_new (
/* We can use inode numbers when the file exists. Works better
* for hard links. */
FileID file_id;
- bool file_id_valid = (sfname != NULL &&
- os_fileid((char *)sfname, &file_id));
+ bool file_id_valid = (sfname != NULL
+ && os_fileid((char *)sfname, &file_id));
if (ffname != NULL && !(flags & BLN_DUMMY)
&& (buf = buflist_findname_file_id(ffname, &file_id,
file_id_valid)) != NULL) {
@@ -1546,6 +1553,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_ep);
clear_string_option(&buf->b_p_path);
clear_string_option(&buf->b_p_tags);
+ clear_string_option(&buf->b_p_tc);
clear_string_option(&buf->b_p_dict);
clear_string_option(&buf->b_p_tsr);
clear_string_option(&buf->b_p_qe);
@@ -1601,21 +1609,28 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
col = 0;
if (options & GETF_SWITCH) {
- /* If 'switchbuf' contains "useopen": jump to first window containing
- * "buf" if one exists */
- if (swb_flags & SWB_USEOPEN)
+ // If 'switchbuf' contains "useopen": jump to first window containing
+ // "buf" if one exists
+ if (swb_flags & SWB_USEOPEN) {
wp = buf_jump_open_win(buf);
- /* If 'switchbuf' contains "usetab": jump to first window in any tab
- * page containing "buf" if one exists */
- if (wp == NULL && (swb_flags & SWB_USETAB))
+ }
+
+ // If 'switchbuf' contains "usetab": jump to first window in any tab
+ // page containing "buf" if one exists
+ if (wp == NULL && (swb_flags & SWB_USETAB)) {
wp = buf_jump_open_tab(buf);
- /* If 'switchbuf' contains "split" or "newtab" and the current buffer
- * isn't empty: open new window */
- if (wp == NULL && (swb_flags & (SWB_SPLIT | SWB_NEWTAB)) && !bufempty()) {
- if (swb_flags & SWB_NEWTAB) /* Open in a new tab */
+ }
+
+ // If 'switchbuf' contains "split", "vsplit" or "newtab" and the
+ // current buffer isn't empty: open new tab or window
+ if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB))
+ && !bufempty()) {
+ if (swb_flags & SWB_NEWTAB) {
tabpage_new();
- else if (win_split(0, 0) == FAIL) /* Open in a new window */
+ } else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0)
+ == FAIL) {
return FAIL;
+ }
RESET_BINDING(curwin);
}
}
@@ -1734,12 +1749,15 @@ int buflist_findpat(
int toggledollar;
if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#')) {
- if (*pattern == '%')
+ if (*pattern == '%') {
match = curbuf->b_fnum;
- else
+ } else {
match = curwin->w_alt_fnum;
- if (diffmode && !diff_mode_buf(buflist_findnr(match)))
+ }
+ buf_T *found_buf = buflist_findnr(match);
+ if (diffmode && !(found_buf && diff_mode_buf(found_buf))) {
match = -1;
+ }
}
/*
* Try four ways of matching a listed buffer:
@@ -2044,16 +2062,15 @@ void buflist_setfpos(buf_T *const buf, win_T *const win,
}
-/*
- * Return true when "wip" has 'diff' set and the diff is only for another tab
- * page. That's because a diff is local to a tab page.
- */
+/// Check that "wip" has 'diff' set and the diff is only for another tab page.
+/// That's because a diff is local to a tab page.
static bool wininfo_other_tab_diff(wininfo_T *wip)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (wip->wi_opt.wo_diff) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- /* return false when it's a window in the current tab page, thus
- * the buffer was in diff mode here */
+ // return false when it's a window in the current tab page, thus
+ // the buffer was in diff mode here
if (wip->wi_win == wp) {
return false;
}
@@ -2178,15 +2195,16 @@ void buflist_list(exarg_T *eap)
len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"",
buf->b_fnum,
buf->b_p_bl ? ' ' : 'u',
- buf == curbuf ? '%' :
- (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '),
- buf->b_ml.ml_mfp == NULL ? ' ' :
- (buf->b_nwindows == 0 ? 'h' : 'a'),
+ buf == curbuf ? '%' : (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '),
+ buf->b_ml.ml_mfp == NULL ? ' ' : (buf->b_nwindows == 0 ? 'h' : 'a'),
!MODIFIABLE(buf) ? '-' : (buf->b_p_ro ? '=' : ' '),
- (buf->b_flags & BF_READERR) ? 'x'
- : (bufIsChanged(buf) ? '+' : ' '),
+ (buf->b_flags & BF_READERR) ? 'x' : (bufIsChanged(buf) ? '+' : ' '),
NameBuff);
+ if (len > IOSIZE - 20) {
+ len = IOSIZE - 20;
+ }
+
/* put "line 999" in column 40 or after the file name */
i = 40 - vim_strsize(IObuff);
do {
@@ -2410,52 +2428,62 @@ void buflist_altfpos(win_T *win)
buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, TRUE);
}
-/*
- * Return TRUE if 'ffname' is not the same file as current file.
- * Fname must have a full path (expanded by path_get_absolute_path()).
- */
-int otherfile(char_u *ffname)
+/// Check that "ffname" is not the same file as current file.
+/// Fname must have a full path (expanded by path_get_absolute_path()).
+///
+/// @param ffname full path name to check
+bool otherfile(char_u *ffname)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
return otherfile_buf(curbuf, ffname, NULL, false);
}
-static int otherfile_buf(buf_T *buf, char_u *ffname,
- FileID *file_id_p, bool file_id_valid)
+/// Check that "ffname" is not the same file as the file loaded in "buf".
+/// Fname must have a full path (expanded by path_get_absolute_path()).
+///
+/// @param buf buffer to check
+/// @param ffname full path name to check
+/// @param file_id_p information about the file at "ffname".
+/// @param file_id_valid whether a valid "file_id_p" was passed in.
+static bool otherfile_buf(buf_T *buf, char_u *ffname, FileID *file_id_p,
+ bool file_id_valid)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- /* no name is different */
+ // no name is different
if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) {
- return TRUE;
+ return true;
}
if (fnamecmp(ffname, buf->b_ffname) == 0) {
- return FALSE;
+ return false;
}
{
FileID file_id;
- /* If no struct stat given, get it now */
+ // If no struct stat given, get it now
if (file_id_p == NULL) {
file_id_p = &file_id;
file_id_valid = os_fileid((char *)ffname, file_id_p);
}
if (!file_id_valid) {
// file_id not valid, assume files are different.
- return TRUE;
+ return true;
}
- /* Use dev/ino to check if the files are the same, even when the names
- * are different (possible with links). Still need to compare the
- * name above, for when the file doesn't exist yet.
- * Problem: The dev/ino changes when a file is deleted (and created
- * again) and remains the same when renamed/moved. We don't want to
- * stat() each buffer each time, that would be too slow. Get the
- * dev/ino again when they appear to match, but not when they appear
- * to be different: Could skip a buffer when it's actually the same
- * file. */
+ // Use dev/ino to check if the files are the same, even when the names
+ // are different (possible with links). Still need to compare the
+ // name above, for when the file doesn't exist yet.
+ // Problem: The dev/ino changes when a file is deleted (and created
+ // again) and remains the same when renamed/moved. We don't want to
+ // stat() each buffer each time, that would be too slow. Get the
+ // dev/ino again when they appear to match, but not when they appear
+ // to be different: Could skip a buffer when it's actually the same
+ // file.
if (buf_same_file_id(buf, file_id_p)) {
buf_set_file_id(buf);
- if (buf_same_file_id(buf, file_id_p))
- return FALSE;
+ if (buf_same_file_id(buf, file_id_p)) {
+ return false;
+ }
}
}
- return TRUE;
+ return true;
}
// Set file_id for a buffer.
@@ -2472,11 +2500,14 @@ void buf_set_file_id(buf_T *buf)
}
}
-// return TRUE if file_id in buffer "buf" matches with "file_id".
+/// Check that file_id in buffer "buf" matches with "file_id".
+///
+/// @param buf buffer
+/// @param file_id file id
static bool buf_same_file_id(buf_T *buf, FileID *file_id)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- return buf->file_id_valid
- && os_fileid_equal(&(buf->file_id), file_id);
+ return buf->file_id_valid && os_fileid_equal(&(buf->file_id), file_id);
}
/*
@@ -2759,23 +2790,28 @@ void maketitle(void)
resettitle();
}
-/*
- * Used for title and icon: Check if "str" differs from "*last". Set "*last"
- * from "str" if it does.
- * Return TRUE when "*last" changed.
- */
-static int ti_change(char_u *str, char_u **last)
+/// Used for title and icon: Check if "str" differs from "*last". Set "*last"
+/// from "str" if it does by freeing the old value of "*last" and duplicating
+/// "str".
+///
+/// @param str desired title string
+/// @param[in,out] last current title string
+//
+/// @return true when "*last" changed.
+static bool ti_change(char_u *str, char_u **last)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
if ((str == NULL) != (*last == NULL)
|| (str != NULL && *last != NULL && STRCMP(str, *last) != 0)) {
xfree(*last);
- if (str == NULL)
+ if (str == NULL) {
*last = NULL;
- else
+ } else {
*last = vim_strsave(str);
- return TRUE;
+ }
+ return true;
}
- return FALSE;
+ return false;
}
/*
@@ -3003,7 +3039,7 @@ int build_stl_str_hl(
&& item[groupitem[groupdepth]].minwid == 0) {
bool has_normal_items = false;
for (long n = groupitem[groupdepth] + 1; n < curitem; n++) {
- if (item[n].type == Normal) {
+ if (item[n].type == Normal || item[n].type == Highlight) {
has_normal_items = true;
break;
}
@@ -3895,6 +3931,11 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen)
above = wp->w_topline - 1;
above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill;
+ if (wp->w_topline == 1 && wp->w_topfill >= 1) {
+ // All buffer lines are displayed and there is an indication
+ // of filler lines, that can be considered seeing all lines.
+ above = 0;
+ }
below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1;
if (below <= 0)
STRLCPY(buf, (above == 0 ? _("All") : _("Bot")), buflen);
@@ -3906,26 +3947,29 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen)
: (int)(above * 100L / (above + below)));
}
-/*
- * Append (file 2 of 8) to "buf[buflen]", if editing more than one file.
- * Return TRUE if it was appended.
- */
-static int
-append_arg_number (
- win_T *wp,
- char_u *buf,
- int buflen,
- int add_file /* Add "file" before the arg number */
-)
+/// Append (file 2 of 8) to "buf[buflen]", if editing more than one file.
+///
+/// @param wp window whose buffers to check
+/// @param[in,out] buf string buffer to add the text to
+/// @param buflen length of the string buffer
+/// @param add_file if true, add "file" before the arg number
+///
+/// @return true if it was appended.
+static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file)
+ FUNC_ATTR_NONNULL_ALL
{
- char_u *p;
+ // Nothing to do
+ if (ARGCOUNT <= 1) {
+ return false;
+ }
- if (ARGCOUNT <= 1) /* nothing to do */
- return FALSE;
+ char_u *p = buf + STRLEN(buf); // go to the end of the buffer
+
+ // Early out if the string is getting too long
+ if (p - buf + 35 >= buflen) {
+ return false;
+ }
- p = buf + STRLEN(buf); /* go to the end of the buffer */
- if (p - buf + 35 >= buflen) /* getting too long */
- return FALSE;
*p++ = ' ';
*p++ = '(';
if (add_file) {
@@ -3933,9 +3977,10 @@ append_arg_number (
p += 5;
}
vim_snprintf((char *)p, (size_t)(buflen - (p - buf)),
- wp->w_arg_idx_invalid ? "(%d) of %d)"
- : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT);
- return TRUE;
+ wp->w_arg_idx_invalid
+ ? "(%d) of %d)"
+ : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT);
+ return true;
}
/*
@@ -4569,11 +4614,16 @@ char_u *buf_spname(buf_T *buf)
return NULL;
}
-/*
- * Find a window for buffer "buf".
- * If found true is returned and "wp" and "tp" are set to the window and tabpage.
- * If not found false is returned.
- */
+/// Find a window for buffer "buf".
+/// If found true is returned and "wp" and "tp" are set to
+/// the window and tabpage.
+/// If not found, false is returned.
+///
+/// @param buf buffer to find a window for
+/// @param[out] wp stores the found window
+/// @param[out] tp stores the found tabpage
+///
+/// @return true if a window was found for the buffer.
bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp)
{
*wp = NULL;
@@ -4855,6 +4905,224 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a
}
}
+// bufhl: plugin highlights associated with a buffer
+
+/// Adds a highlight to buffer.
+///
+/// Unlike matchaddpos() highlights follow changes to line numbering (as lines
+/// are inserted/removed above the highlighted line), like signs and marks do.
+///
+/// When called with "src_id" set to 0, a unique source id is generated and
+/// returned. Succesive calls can pass it in as "src_id" to add new highlights
+/// to the same source group. All highlights in the same group can be cleared
+/// at once. If the highlight never will be manually deleted pass in -1 for
+/// "src_id"
+///
+/// if "hl_id" or "lnum" is invalid no highlight is added, but a new src_id
+/// is still returned.
+///
+/// @param buf The buffer to add highlights to
+/// @param src_id src_id to use or 0 to use a new src_id group,
+/// or -1 for ungrouped highlight.
+/// @param hl_id Id of the highlight group to use
+/// @param lnum The line to highlight
+/// @param col_start First column to highlight
+/// @param col_end The last column to highlight,
+/// or -1 to highlight to end of line
+/// @return The src_id that was used
+int bufhl_add_hl(buf_T *buf,
+ int src_id,
+ int hl_id,
+ linenr_T lnum,
+ colnr_T col_start,
+ colnr_T col_end) {
+ static int next_src_id = 1;
+ if (src_id == 0) {
+ src_id = next_src_id++;
+ }
+ if (hl_id <= 0) {
+ // no highlight group or invalid line, just return src_id
+ return src_id;
+ }
+ if (!buf->b_bufhl_info) {
+ buf->b_bufhl_info = map_new(linenr_T, bufhl_vec_T)();
+ }
+ bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(buf->b_bufhl_info,
+ lnum, true);
+
+ bufhl_hl_item_T *hlentry = kv_pushp(bufhl_hl_item_T, *lineinfo);
+ hlentry->src_id = src_id;
+ hlentry->hl_id = hl_id;
+ hlentry->start = col_start;
+ hlentry->stop = col_end;
+
+ if (0 < lnum && lnum <= buf->b_ml.ml_line_count) {
+ changed_lines_buf(buf, lnum, lnum+1, 0);
+ redraw_buf_later(buf, VALID);
+ }
+ return src_id;
+}
+
+/// Clear bufhl highlights from a given source group and range of lines.
+///
+/// @param buf The buffer to remove highlights from
+/// @param src_id highlight source group to clear, or -1 to clear all groups.
+/// @param line_start first line to clear
+/// @param line_end last line to clear or MAXLNUM to clear to end of file.
+void bufhl_clear_line_range(buf_T *buf,
+ int src_id,
+ linenr_T line_start,
+ linenr_T line_end) {
+ if (!buf->b_bufhl_info) {
+ return;
+ }
+ linenr_T line;
+ linenr_T first_changed = MAXLNUM, last_changed = -1;
+ // In the case line_start - line_end << bufhl_info->size
+ // it might be better to reverse this, i e loop over the lines
+ // to clear on.
+ bufhl_vec_T unused;
+ map_foreach(buf->b_bufhl_info, line, unused, {
+ (void)unused;
+ if (line_start <= line && line <= line_end) {
+ if (bufhl_clear_line(buf->b_bufhl_info, src_id, line)) {
+ if (line > last_changed) {
+ last_changed = line;
+ }
+ if (line < first_changed) {
+ first_changed = line;
+ }
+ }
+ }
+ })
+
+ if (last_changed != -1) {
+ changed_lines_buf(buf, first_changed, last_changed+1, 0);
+ redraw_buf_later(buf, VALID);
+ }
+}
+
+/// Clear bufhl highlights from a given source group and given line
+///
+/// @param bufhl_info The highlight info for the buffer
+/// @param src_id Highlight source group to clear, or -1 to clear all groups.
+/// @param lnum Linenr where the highlight should be cleared
+static bool bufhl_clear_line(bufhl_info_T *bufhl_info, int src_id, int lnum) {
+ bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(bufhl_info,
+ lnum, false);
+ size_t oldsize = kv_size(*lineinfo);
+ if (src_id < 0) {
+ kv_size(*lineinfo) = 0;
+ } else {
+ size_t newind = 0;
+ for (size_t i = 0; i < kv_size(*lineinfo); i++) {
+ if (kv_A(*lineinfo, i).src_id != src_id) {
+ if (i != newind) {
+ kv_A(*lineinfo, newind) = kv_A(*lineinfo, i);
+ }
+ newind++;
+ }
+ }
+ kv_size(*lineinfo) = newind;
+ }
+
+ if (kv_size(*lineinfo) == 0) {
+ kv_destroy(*lineinfo);
+ map_del(linenr_T, bufhl_vec_T)(bufhl_info, lnum);
+ }
+ return kv_size(*lineinfo) != oldsize;
+}
+
+/// Remove all highlights and free the highlight data
+void bufhl_clear_all(buf_T* buf) {
+ if (!buf->b_bufhl_info) {
+ return;
+ }
+ bufhl_clear_line_range(buf, -1, 1, MAXLNUM);
+ map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info);
+ buf->b_bufhl_info = NULL;
+}
+
+/// Adjust a placed highlight for inserted/deleted lines.
+void bufhl_mark_adjust(buf_T* buf,
+ linenr_T line1,
+ linenr_T line2,
+ long amount,
+ long amount_after) {
+ if (!buf->b_bufhl_info) {
+ return;
+ }
+
+ bufhl_info_T *newmap = map_new(linenr_T, bufhl_vec_T)();
+ linenr_T line;
+ bufhl_vec_T lineinfo;
+ map_foreach(buf->b_bufhl_info, line, lineinfo, {
+ if (line >= line1 && line <= line2) {
+ if (amount == MAXLNUM) {
+ bufhl_clear_line(buf->b_bufhl_info, -1, line);
+ continue;
+ } else {
+ line += amount;
+ }
+ } else if (line > line2) {
+ line += amount_after;
+ }
+ map_put(linenr_T, bufhl_vec_T)(newmap, line, lineinfo);
+ });
+ map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info);
+ buf->b_bufhl_info = newmap;
+}
+
+
+/// Get highlights to display at a specific line
+///
+/// @param buf The buffer handle
+/// @param lnum The line number
+/// @param[out] info The highligts for the line
+/// @return true if there was highlights to display
+bool bufhl_start_line(buf_T *buf, linenr_T lnum, bufhl_lineinfo_T *info) {
+ if (!buf->b_bufhl_info) {
+ return false;
+ }
+
+ info->valid_to = -1;
+ info->entries = map_get(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, lnum);
+ return kv_size(info->entries) > 0;
+}
+
+/// get highlighting at column col
+///
+/// It is is assumed this will be called with
+/// non-decreasing column nrs, so that it is
+/// possible to only recalculate highlights
+/// at endpoints.
+///
+/// @param info The info returned by bufhl_start_line
+/// @param col The column to get the attr for
+/// @return The highilight attr to display at the column
+int bufhl_get_attr(bufhl_lineinfo_T *info, colnr_T col) {
+ if (col <= info->valid_to) {
+ return info->current;
+ }
+ int attr = 0;
+ info->valid_to = MAXCOL;
+ for (size_t i = 0; i < kv_size(info->entries); i++) {
+ bufhl_hl_item_T entry = kv_A(info->entries, i);
+ if (entry.start <= col && col <= entry.stop) {
+ int entry_attr = syn_id2attr(entry.hl_id);
+ attr = hl_combine_attr(attr, entry_attr);
+ if (entry.stop < info->valid_to) {
+ info->valid_to = entry.stop;
+ }
+ } else if (col < entry.start && entry.start-1 < info->valid_to) {
+ info->valid_to = entry.start-1;
+ }
+ }
+ info->current = attr;
+ return attr;
+}
+
+
/*
* Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed.
*/
@@ -4869,50 +5137,54 @@ void set_buflisted(int on)
}
}
-/*
- * Read the file for "buf" again and check if the contents changed.
- * Return TRUE if it changed or this could not be checked.
- */
-int buf_contents_changed(buf_T *buf)
+/// Read the file for "buf" again and check if the contents changed.
+/// Return true if it changed or this could not be checked.
+///
+/// @param buf buffer to check
+///
+/// @return true if the buffer's contents have changed
+bool buf_contents_changed(buf_T *buf)
+ FUNC_ATTR_NONNULL_ALL
{
- buf_T *newbuf;
- int differ = TRUE;
- linenr_T lnum;
- aco_save_T aco;
- exarg_T ea;
+ bool differ = true;
- /* Allocate a buffer without putting it in the buffer list. */
- newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
- if (newbuf == NULL)
- return TRUE;
+ // Allocate a buffer without putting it in the buffer list.
+ buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
+ if (newbuf == NULL) {
+ return true;
+ }
- /* Force the 'fileencoding' and 'fileformat' to be equal. */
+ // Force the 'fileencoding' and 'fileformat' to be equal.
+ exarg_T ea;
prep_exarg(&ea, buf);
- /* set curwin/curbuf to buf and save a few things */
+ // set curwin/curbuf to buf and save a few things
+ aco_save_T aco;
aucmd_prepbuf(&aco, newbuf);
if (ml_open(curbuf) == OK
&& readfile(buf->b_ffname, buf->b_fname,
- (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
- &ea, READ_NEW | READ_DUMMY) == OK) {
- /* compare the two files line by line */
+ (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
+ &ea, READ_NEW | READ_DUMMY) == OK) {
+ // compare the two files line by line
if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) {
- differ = FALSE;
- for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
- if (STRCMP(ml_get_buf(buf, lnum, FALSE), ml_get(lnum)) != 0) {
- differ = TRUE;
+ differ = false;
+ for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) {
+ if (STRCMP(ml_get_buf(buf, lnum, false), ml_get(lnum)) != 0) {
+ differ = true;
break;
}
+ }
}
}
xfree(ea.cmd);
- /* restore curwin/curbuf and a few other things */
+ // restore curwin/curbuf and a few other things
aucmd_restbuf(&aco);
- if (curbuf != newbuf) /* safety check */
- wipe_buffer(newbuf, FALSE);
+ if (curbuf != newbuf) { // safety check
+ wipe_buffer(newbuf, false);
+ }
return differ;
}
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 78d9a9484e..0324f6b88a 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -28,6 +28,8 @@ typedef struct file_buffer buf_T; // Forward declaration
#include "nvim/profile.h"
// for String
#include "nvim/api/private/defs.h"
+// for Map(K, V)
+#include "nvim/map.h"
#define MODIFIABLE(buf) (!buf->terminal && buf->b_p_ma)
@@ -59,21 +61,21 @@ typedef struct file_buffer buf_T; // Forward declaration
#define VALID_BOTLINE_AP 0x40 /* w_botine is approximated */
#define VALID_TOPLINE 0x80 /* w_topline is valid (for cursor position) */
-/* flags for b_flags */
-#define BF_RECOVERED 0x01 /* buffer has been recovered */
-#define BF_CHECK_RO 0x02 /* need to check readonly when loading file
- into buffer (set by ":e", may be reset by
- ":buf" */
-#define BF_NEVERLOADED 0x04 /* file has never been loaded into buffer,
- many variables still need to be set */
-#define BF_NOTEDITED 0x08 /* Set when file name is changed after
- starting to edit, reset when file is
- written out. */
-#define BF_NEW 0x10 /* file didn't exist when editing started */
-#define BF_NEW_W 0x20 /* Warned for BF_NEW and file created */
-#define BF_READERR 0x40 /* got errors while reading the file */
-#define BF_DUMMY 0x80 /* dummy buffer, only used internally */
-#define BF_PRESERVED 0x100 /* ":preserve" was used */
+// flags for b_flags
+#define BF_RECOVERED 0x01 // buffer has been recovered
+#define BF_CHECK_RO 0x02 // need to check readonly when loading file
+ // into buffer (set by ":e", may be reset by
+ // ":buf")
+#define BF_NEVERLOADED 0x04 // file has never been loaded into buffer,
+ // many variables still need to be set
+#define BF_NOTEDITED 0x08 // Set when file name is changed after
+ // starting to edit, reset when file is
+ // written out.
+#define BF_NEW 0x10 // file didn't exist when editing started
+#define BF_NEW_W 0x20 // Warned for BF_NEW and file created
+#define BF_READERR 0x40 // got errors while reading the file
+#define BF_DUMMY 0x80 // dummy buffer, only used internally
+#define BF_PRESERVED 0x100 // ":preserve" was used
/* Mask to check for flags that prevent normal writing */
#define BF_WRITE_MASK (BF_NOTEDITED + BF_NEW + BF_READERR)
@@ -101,6 +103,11 @@ typedef int scid_T; /* script ID */
// for signlist_T
#include "nvim/sign_defs.h"
+// for bufhl_*_T
+#include "nvim/bufhl_defs.h"
+
+typedef Map(linenr_T, bufhl_vec_T) bufhl_info_T;
+
// for FileID
#include "nvim/os/fs_defs.h"
@@ -134,8 +141,8 @@ struct buffblock {
struct buffheader {
buffblock_T bh_first; // first (dummy) block of list
buffblock_T *bh_curr; // buffblock for appending
- int bh_index; // index for reading
- int bh_space; // space in bh_curr for appending
+ size_t bh_index; // index for reading
+ size_t bh_space; // space in bh_curr for appending
};
/*
@@ -526,9 +533,9 @@ struct file_buffer {
/*
* Character table, only used in charset.c for 'iskeyword'
- * 32 bytes of 8 bits: 1 bit per character 0-255.
+ * bitset with 4*64=256 bits: 1 bit per character 0-255.
*/
- char_u b_chartab[32];
+ uint64_t b_chartab[4];
/* Table used for mappings local to a buffer. */
mapblock_T *(b_maphash[256]);
@@ -592,85 +599,88 @@ struct file_buffer {
int b_p_scriptID[BV_COUNT]; /* SIDs for buffer-local options */
- int b_p_ai; /* 'autoindent' */
- int b_p_ai_nopaste; /* b_p_ai saved for paste mode */
- char_u *b_p_bkc; ///< 'backupcopy'
- unsigned int b_bkc_flags; ///< flags for 'backupcopy'
- int b_p_ci; /* 'copyindent' */
- int b_p_bin; /* 'binary' */
- int b_p_bomb; /* 'bomb' */
- char_u *b_p_bh; /* 'bufhidden' */
- char_u *b_p_bt; /* 'buftype' */
- int b_p_bl; /* 'buflisted' */
- int b_p_cin; /* 'cindent' */
- char_u *b_p_cino; /* 'cinoptions' */
- char_u *b_p_cink; /* 'cinkeys' */
- char_u *b_p_cinw; /* 'cinwords' */
- char_u *b_p_com; /* 'comments' */
- char_u *b_p_cms; /* 'commentstring' */
- char_u *b_p_cpt; /* 'complete' */
- char_u *b_p_cfu; /* 'completefunc' */
- char_u *b_p_ofu; /* 'omnifunc' */
- int b_p_eol; /* 'endofline' */
- int b_p_fixeol; /* 'fixendofline' */
- int b_p_et; /* 'expandtab' */
- int b_p_et_nobin; /* b_p_et saved for binary mode */
- char_u *b_p_fenc; /* 'fileencoding' */
- char_u *b_p_ff; /* 'fileformat' */
- char_u *b_p_ft; /* 'filetype' */
- char_u *b_p_fo; /* 'formatoptions' */
- char_u *b_p_flp; /* 'formatlistpat' */
- int b_p_inf; /* 'infercase' */
- char_u *b_p_isk; /* 'iskeyword' */
- char_u *b_p_def; /* 'define' local value */
- char_u *b_p_inc; /* 'include' */
- char_u *b_p_inex; /* 'includeexpr' */
- uint32_t b_p_inex_flags; /* flags for 'includeexpr' */
- char_u *b_p_inde; /* 'indentexpr' */
- uint32_t b_p_inde_flags; /* flags for 'indentexpr' */
- char_u *b_p_indk; /* 'indentkeys' */
- char_u *b_p_fex; /* 'formatexpr' */
- uint32_t b_p_fex_flags; /* flags for 'formatexpr' */
- char_u *b_p_kp; /* 'keywordprg' */
- int b_p_lisp; /* 'lisp' */
- char_u *b_p_mps; /* 'matchpairs' */
- int b_p_ml; /* 'modeline' */
- int b_p_ml_nobin; /* b_p_ml saved for binary mode */
- int b_p_ma; /* 'modifiable' */
- char_u *b_p_nf; /* 'nrformats' */
- int b_p_pi; /* 'preserveindent' */
- char_u *b_p_qe; /* 'quoteescape' */
- int b_p_ro; /* 'readonly' */
- long b_p_sw; /* 'shiftwidth' */
- int b_p_si; /* 'smartindent' */
- long b_p_sts; /* 'softtabstop' */
- long b_p_sts_nopaste; /* b_p_sts saved for paste mode */
- char_u *b_p_sua; /* 'suffixesadd' */
- bool b_p_swf; /* 'swapfile' */
- long b_p_smc; /* 'synmaxcol' */
- char_u *b_p_syn; /* 'syntax' */
- long b_p_ts; /* 'tabstop' */
- long b_p_tw; /* 'textwidth' */
- long b_p_tw_nobin; /* b_p_tw saved for binary mode */
- long b_p_tw_nopaste; /* b_p_tw saved for paste mode */
- long b_p_wm; /* 'wrapmargin' */
- long b_p_wm_nobin; /* b_p_wm saved for binary mode */
- long b_p_wm_nopaste; /* b_p_wm saved for paste mode */
- char_u *b_p_keymap; /* 'keymap' */
-
- /* local values for options which are normally global */
- char_u *b_p_gp; /* 'grepprg' local value */
- char_u *b_p_mp; /* 'makeprg' local value */
- char_u *b_p_efm; /* 'errorformat' local value */
- char_u *b_p_ep; /* 'equalprg' local value */
- char_u *b_p_path; /* 'path' local value */
- int b_p_ar; /* 'autoread' local value */
- char_u *b_p_tags; /* 'tags' local value */
- char_u *b_p_dict; /* 'dictionary' local value */
- char_u *b_p_tsr; /* 'thesaurus' local value */
- long b_p_ul; /* 'undolevels' local value */
- int b_p_udf; /* 'undofile' */
- char_u *b_p_lw; // 'lispwords' local value
+ int b_p_ai; ///< 'autoindent'
+ int b_p_ai_nopaste; ///< b_p_ai saved for paste mode
+ char_u *b_p_bkc; ///< 'backupco
+ unsigned int b_bkc_flags; ///< flags for 'backupco
+ int b_p_ci; ///< 'copyindent'
+ int b_p_bin; ///< 'binary'
+ int b_p_bomb; ///< 'bomb'
+ char_u *b_p_bh; ///< 'bufhidden'
+ char_u *b_p_bt; ///< 'buftype'
+ int b_p_bl; ///< 'buflisted'
+ int b_p_cin; ///< 'cindent'
+ char_u *b_p_cino; ///< 'cinoptions'
+ char_u *b_p_cink; ///< 'cinkeys'
+ char_u *b_p_cinw; ///< 'cinwords'
+ char_u *b_p_com; ///< 'comments'
+ char_u *b_p_cms; ///< 'commentstring'
+ char_u *b_p_cpt; ///< 'complete'
+ char_u *b_p_cfu; ///< 'completefunc'
+ char_u *b_p_ofu; ///< 'omnifunc'
+ int b_p_eol; ///< 'endofline'
+ int b_p_fixeol; ///< 'fixendofline'
+ int b_p_et; ///< 'expandtab'
+ int b_p_et_nobin; ///< b_p_et saved for binary mode
+ int b_p_et_nopaste; ///< b_p_et saved for paste mode
+ char_u *b_p_fenc; ///< 'fileencoding'
+ char_u *b_p_ff; ///< 'fileformat'
+ char_u *b_p_ft; ///< 'filetype'
+ char_u *b_p_fo; ///< 'formatoptions'
+ char_u *b_p_flp; ///< 'formatlistpat'
+ int b_p_inf; ///< 'infercase'
+ char_u *b_p_isk; ///< 'iskeyword'
+ char_u *b_p_def; ///< 'define' local value
+ char_u *b_p_inc; ///< 'include'
+ char_u *b_p_inex; ///< 'includeexpr'
+ uint32_t b_p_inex_flags; ///< flags for 'includeexpr'
+ char_u *b_p_inde; ///< 'indentexpr'
+ uint32_t b_p_inde_flags; ///< flags for 'indentexpr'
+ char_u *b_p_indk; ///< 'indentkeys'
+ char_u *b_p_fex; ///< 'formatexpr'
+ uint32_t b_p_fex_flags; ///< flags for 'formatexpr'
+ char_u *b_p_kp; ///< 'keywordprg'
+ int b_p_lisp; ///< 'lisp'
+ char_u *b_p_mps; ///< 'matchpairs'
+ int b_p_ml; ///< 'modeline'
+ int b_p_ml_nobin; ///< b_p_ml saved for binary mode
+ int b_p_ma; ///< 'modifiable'
+ char_u *b_p_nf; ///< 'nrformats'
+ int b_p_pi; ///< 'preserveindent'
+ char_u *b_p_qe; ///< 'quoteescape'
+ int b_p_ro; ///< 'readonly'
+ long b_p_sw; ///< 'shiftwidth'
+ int b_p_si; ///< 'smartindent'
+ long b_p_sts; ///< 'softtabstop'
+ long b_p_sts_nopaste; ///< b_p_sts saved for paste mode
+ char_u *b_p_sua; ///< 'suffixesadd'
+ bool b_p_swf; ///< 'swapfile'
+ long b_p_smc; ///< 'synmaxcol'
+ char_u *b_p_syn; ///< 'syntax'
+ long b_p_ts; ///< 'tabstop'
+ long b_p_tw; ///< 'textwidth'
+ long b_p_tw_nobin; ///< b_p_tw saved for binary mode
+ long b_p_tw_nopaste; ///< b_p_tw saved for paste mode
+ long b_p_wm; ///< 'wrapmargin'
+ long b_p_wm_nobin; ///< b_p_wm saved for binary mode
+ long b_p_wm_nopaste; ///< b_p_wm saved for paste mode
+ char_u *b_p_keymap; ///< 'keymap'
+
+ // local values for options which are normally global
+ char_u *b_p_gp; ///< 'grepprg' local value
+ char_u *b_p_mp; ///< 'makeprg' local value
+ char_u *b_p_efm; ///< 'errorformat' local value
+ char_u *b_p_ep; ///< 'equalprg' local value
+ char_u *b_p_path; ///< 'path' local value
+ int b_p_ar; ///< 'autoread' local value
+ char_u *b_p_tags; ///< 'tags' local value
+ char_u *b_p_tc; ///< 'tagcase' local value
+ unsigned b_tc_flags; ///< flags for 'tagcase'
+ char_u *b_p_dict; ///< 'dictionary' local value
+ char_u *b_p_tsr; ///< 'thesaurus' local value
+ long b_p_ul; ///< 'undolevels' local value
+ int b_p_udf; ///< 'undofile'
+ char_u *b_p_lw; ///< 'lispwords' local value
/* end of buffer options */
@@ -753,6 +763,8 @@ struct file_buffer {
dict_T *additional_data; // Additional data from shada file if any.
int b_mapped_ctrl_c; // modes where CTRL-C is mapped
+
+ bufhl_info_T *b_bufhl_info; // buffer stored highlights
};
/*
@@ -806,10 +818,12 @@ struct tabpage_S {
was set */
diff_T *tp_first_diff;
buf_T *(tp_diffbuf[DB_COUNT]);
- int tp_diff_invalid; /* list of diffs is outdated */
- frame_T *(tp_snapshot[SNAP_COUNT]); /* window layout snapshots */
- dictitem_T tp_winvar; /* variable for "t:" Dictionary */
- dict_T *tp_vars; /* internal variables, local to tab page */
+ int tp_diff_invalid; ///< list of diffs is outdated */
+ frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots
+ dictitem_T tp_winvar; ///< variable for "t:" Dictionary
+ dict_T *tp_vars; ///< internal variables, local to tab page
+ char_u *localdir; ///< Absolute path of local directory or
+ ///< NULL
};
/*
@@ -904,13 +918,14 @@ struct posmatch
typedef struct matchitem matchitem_T;
struct matchitem {
matchitem_T *next;
- int id; /* match ID */
- int priority; /* match priority */
- char_u *pattern; /* pattern to highlight */
- int hlg_id; /* highlight group ID */
- regmmatch_T match; /* regexp program for pattern */
- posmatch_T pos; // position matches
- match_T hl; /* struct for doing the actual highlighting */
+ int id; ///< match ID
+ int priority; ///< match priority
+ char_u *pattern; ///< pattern to highlight
+ int hlg_id; ///< highlight group ID
+ regmmatch_T match; ///< regexp program for pattern
+ posmatch_T pos; ///< position matches
+ match_T hl; ///< struct for doing the actual highlighting
+ int conceal_char; ///< cchar for Conceal highlighting
};
/*
@@ -942,16 +957,14 @@ struct window_S {
time through cursupdate() to the
current virtual column */
- /*
- * the next six are used to update the visual part
- */
- char w_old_visual_mode; /* last known VIsual_mode */
- linenr_T w_old_cursor_lnum; /* last known end of visual part */
- colnr_T w_old_cursor_fcol; /* first column for block visual part */
- colnr_T w_old_cursor_lcol; /* last column for block visual part */
- linenr_T w_old_visual_lnum; /* last known start of visual part */
- colnr_T w_old_visual_col; /* last known start of visual part */
- colnr_T w_old_curswant; /* last known value of Curswant */
+ // the next seven are used to update the visual part
+ char w_old_visual_mode; ///< last known VIsual_mode
+ linenr_T w_old_cursor_lnum; ///< last known end of visual part
+ colnr_T w_old_cursor_fcol; ///< first column for block visual part
+ colnr_T w_old_cursor_lcol; ///< last column for block visual part
+ linenr_T w_old_visual_lnum; ///< last known start of visual part
+ colnr_T w_old_visual_col; ///< last known start of visual part
+ colnr_T w_old_curswant; ///< last known value of Curswant
/*
* "w_topline", "w_leftcol" and "w_skipcol" specify the offsets for
diff --git a/src/nvim/bufhl_defs.h b/src/nvim/bufhl_defs.h
new file mode 100644
index 0000000000..e47bb2a238
--- /dev/null
+++ b/src/nvim/bufhl_defs.h
@@ -0,0 +1,25 @@
+#ifndef NVIM_BUFHL_DEFS_H
+#define NVIM_BUFHL_DEFS_H
+
+#include "nvim/pos.h"
+#include "nvim/lib/kvec.h"
+// bufhl: buffer specific highlighting
+
+struct bufhl_hl_item
+{
+ int src_id;
+ int hl_id; // highlight group
+ colnr_T start; // first column to highlight
+ colnr_T stop; // last column to highlight
+};
+typedef struct bufhl_hl_item bufhl_hl_item_T;
+
+typedef kvec_t(struct bufhl_hl_item) bufhl_vec_T;
+
+typedef struct {
+ bufhl_vec_T entries;
+ int current;
+ colnr_T valid_to;
+} bufhl_lineinfo_T;
+
+#endif // NVIM_BUFHL_DEFS_H
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 9a0e1440cc..d0dc7b66fc 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -32,16 +32,16 @@
#endif
-static int chartab_initialized = FALSE;
+static bool chartab_initialized = false;
-// b_chartab[] is an array of 32 bytes, each bit representing one of the
+// b_chartab[] is an array with 256 bits, each bit representing one of the
// characters 0-255.
#define SET_CHARTAB(buf, c) \
- (buf)->b_chartab[(unsigned)(c) >> 3] |= (1 << ((c) & 0x7))
+ (buf)->b_chartab[(unsigned)(c) >> 6] |= (1ull << ((c) & 0x3f))
#define RESET_CHARTAB(buf, c) \
- (buf)->b_chartab[(unsigned)(c) >> 3] &= ~(1 << ((c) & 0x7))
+ (buf)->b_chartab[(unsigned)(c) >> 6] &= ~(1ull << ((c) & 0x3f))
#define GET_CHARTAB(buf, c) \
- ((buf)->b_chartab[(unsigned)(c) >> 3] & (1 << ((c) & 0x7)))
+ ((buf)->b_chartab[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f)))
/// Fill chartab[]. Also fills curbuf->b_chartab[] with flags for keyword
/// characters for current buffer.
@@ -69,12 +69,12 @@ static int chartab_initialized = FALSE;
/// an error, OK otherwise.
int init_chartab(void)
{
- return buf_init_chartab(curbuf, TRUE);
+ return buf_init_chartab(curbuf, true);
}
/// Helper for init_chartab
///
-/// @param global FALSE: only set buf->b_chartab[]
+/// @param global false: only set buf->b_chartab[]
///
/// @return FAIL if 'iskeyword', 'isident', 'isfname' or 'isprint' option has
/// an error, OK otherwise.
@@ -84,13 +84,13 @@ int buf_init_chartab(buf_T *buf, int global)
int c2;
char_u *p;
int i;
- int tilde;
- int do_isalpha;
+ bool tilde;
+ bool do_isalpha;
if (global) {
// Set the default size for printable characters:
// From <Space> to '~' is 1 (printable), others are 2 (not printable).
- // This also inits all 'isident' and 'isfname' flags to FALSE.
+ // This also inits all 'isident' and 'isfname' flags to false.
c = 0;
while (c < ' ') {
@@ -133,7 +133,7 @@ int buf_init_chartab(buf_T *buf, int global)
}
}
- // Init word char flags all to FALSE
+ // Init word char flags all to false
memset(buf->b_chartab, 0, (size_t)32);
if (enc_dbcs != 0) {
@@ -169,11 +169,11 @@ int buf_init_chartab(buf_T *buf, int global)
}
while (*p) {
- tilde = FALSE;
- do_isalpha = FALSE;
+ tilde = false;
+ do_isalpha = false;
if ((*p == '^') && (p[1] != NUL)) {
- tilde = TRUE;
+ tilde = true;
++p;
}
@@ -212,7 +212,7 @@ int buf_init_chartab(buf_T *buf, int global)
// standard function isalpha(). This takes care of locale for
// single-byte characters).
if (c == '@') {
- do_isalpha = TRUE;
+ do_isalpha = true;
c = 1;
c2 = 255;
} else {
@@ -231,7 +231,7 @@ int buf_init_chartab(buf_T *buf, int global)
if (i == 0) {
// (re)set ID flag
if (tilde) {
- chartab[c] &= ~CT_ID_CHAR;
+ chartab[c] &= (uint8_t)~CT_ID_CHAR;
} else {
chartab[c] |= CT_ID_CHAR;
}
@@ -244,18 +244,18 @@ int buf_init_chartab(buf_T *buf, int global)
|| (p_altkeymap && (F_isalpha(c) || F_isdigit(c))))
&& !(enc_dbcs && (MB_BYTE2LEN(c) == 2))) {
if (tilde) {
- chartab[c] = (chartab[c] & ~CT_CELL_MASK)
- + ((dy_flags & DY_UHEX) ? 4 : 2);
- chartab[c] &= ~CT_PRINT_CHAR;
+ chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK)
+ + ((dy_flags & DY_UHEX) ? 4 : 2));
+ chartab[c] &= (uint8_t)~CT_PRINT_CHAR;
} else {
- chartab[c] = (chartab[c] & ~CT_CELL_MASK) + 1;
+ chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK) + 1);
chartab[c] |= CT_PRINT_CHAR;
}
}
} else if (i == 2) {
// (re)set fname flag
if (tilde) {
- chartab[c] &= ~CT_FNAME_CHAR;
+ chartab[c] &= (uint8_t)~CT_FNAME_CHAR;
} else {
chartab[c] |= CT_FNAME_CHAR;
}
@@ -280,7 +280,7 @@ int buf_init_chartab(buf_T *buf, int global)
}
}
}
- chartab_initialized = TRUE;
+ chartab_initialized = true;
return OK;
}
@@ -333,7 +333,8 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET
{
char_u *res;
char_u *p;
- int l, c;
+ int c;
+ size_t l;
char_u hexbuf[11];
if (has_mbyte) {
@@ -343,7 +344,7 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET
p = s;
while (*p != NUL) {
- if ((l = (*mb_ptr2len)(p)) > 1) {
+ if ((l = (size_t)(*mb_ptr2len)(p)) > 1) {
c = (*mb_ptr2char)(p);
p += l;
@@ -354,7 +355,7 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET
len += STRLEN(hexbuf);
}
} else {
- l = byte2cells(*p++);
+ l = (size_t)byte2cells(*p++);
if (l > 0) {
len += l;
@@ -366,14 +367,14 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET
}
res = xmallocz(len);
} else {
- res = xmallocz(vim_strsize(s));
+ res = xmallocz((size_t)vim_strsize(s));
}
*res = NUL;
p = s;
while (*p != NUL) {
- if (has_mbyte && ((l = (*mb_ptr2len)(p)) > 1)) {
+ if (has_mbyte && ((l = (size_t)(*mb_ptr2len)(p)) > 1)) {
c = (*mb_ptr2char)(p);
if (vim_isprintc(c)) {
@@ -477,9 +478,9 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen)
i += (*mb_ptr2len)(STR_PTR(i));
} else {
if (buf == NULL) {
- GA_CHAR(i) = TOLOWER_LOC(GA_CHAR(i));
+ GA_CHAR(i) = (char_u)TOLOWER_LOC(GA_CHAR(i));
} else {
- buf[i] = TOLOWER_LOC(buf[i]);
+ buf[i] = (char_u)TOLOWER_LOC(buf[i]);
}
++i;
}
@@ -493,7 +494,7 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen)
// Catch 22: chartab[] can't be initialized before the options are
// initialized, and initializing options may cause transchar() to be called!
-// When chartab_initialized == FALSE don't use chartab[].
+// When chartab_initialized == false don't use chartab[].
// Does NOT work for multi-byte characters, c must be <= 255.
// Also doesn't work for the first byte of a multi-byte, "c" must be a
// character!
@@ -518,7 +519,7 @@ char_u* transchar(int c)
if ((!chartab_initialized && (((c >= ' ') && (c <= '~')) || F_ischar(c)))
|| ((c < 256) && vim_isprintc_strict(c))) {
// printable character
- transchar_buf[i] = c;
+ transchar_buf[i] = (char_u)c;
transchar_buf[i + 1] = NUL;
} else {
transchar_nonprint(transchar_buf + i, c);
@@ -564,7 +565,7 @@ void transchar_nonprint(char_u *buf, int c)
// 0x00 - 0x1f and 0x7f
buf[0] = '^';
// DEL displayed as ^?
- buf[1] = c ^ 0x40;
+ buf[1] = (char_u)(c ^ 0x40);
buf[2] = NUL;
} else if (enc_utf8 && (c >= 0x80)) {
@@ -572,12 +573,12 @@ void transchar_nonprint(char_u *buf, int c)
} else if ((c >= ' ' + 0x80) && (c <= '~' + 0x80)) {
// 0xa0 - 0xfe
buf[0] = '|';
- buf[1] = c - 0x80;
+ buf[1] = (char_u)(c - 0x80);
buf[2] = NUL;
} else {
// 0x80 - 0x9f and 0xff
buf[0] = '~';
- buf[1] = (c - 0x80) ^ 0x40;
+ buf[1] = (char_u)((c - 0x80) ^ 0x40);
buf[2] = NUL;
}
}
@@ -592,11 +593,11 @@ void transchar_hex(char_u *buf, int c)
buf[0] = '<';
if (c > 255) {
- buf[++i] = nr2hex((unsigned)c >> 12);
- buf[++i] = nr2hex((unsigned)c >> 8);
+ buf[++i] = (char_u)nr2hex((unsigned)c >> 12);
+ buf[++i] = (char_u)nr2hex((unsigned)c >> 8);
}
- buf[++i] = nr2hex((unsigned)c >> 4);
- buf[++i] = nr2hex((unsigned)c);
+ buf[++i] = (char_u)(nr2hex((unsigned)c >> 4));
+ buf[++i] = (char_u)(nr2hex((unsigned)c));
buf[++i] = '>';
buf[++i] = NUL;
}
@@ -734,9 +735,8 @@ int vim_strnsize(char_u *s, int len)
/// @return Number of characters.
#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) { \
- int ts; \
- ts = (buf)->b_p_ts; \
- return (int)(ts - (col % ts)); \
+ const int ts = (int) (buf)->b_p_ts; \
+ return (ts - (int)(col % ts)); \
} else { \
return ptr2cells(p); \
}
@@ -799,32 +799,35 @@ unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len)
return (unsigned int)col;
}
-/// Return TRUE if 'c' is a normal identifier character:
-///
+/// Check that "c" is a normal identifier character:
/// Letters and characters from the 'isident' option.
///
-/// @param c
-///
-/// @return TRUE if 'c' is a normal identifier character.
-int vim_isIDc(int c)
+/// @param c character to check
+bool vim_isIDc(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return c > 0 && c < 0x100 && (chartab[c] & CT_ID_CHAR);
}
-/// return TRUE if 'c' is a keyword character: Letters and characters from
-/// 'iskeyword' option for current buffer.
-///
+/// Check that "c" is a keyword character:
+/// Letters and characters from 'iskeyword' option for current buffer.
/// For multi-byte characters mb_get_class() is used (builtin rules).
///
-/// @param c
-///
-/// @return TRUE if 'c' is a keyword character.
-int vim_iswordc(int c)
+/// @param c character to check
+bool vim_iswordc(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return vim_iswordc_buf(c, curbuf);
}
-int vim_iswordc_buf(int c, buf_T *buf)
+/// Check that "c" is a keyword character:
+/// Letters and characters from 'iskeyword' option for given buffer.
+/// For multi-byte characters mb_get_class() is used (builtin rules).
+///
+/// @param c character to check
+/// @param buf buffer whose keywords to use
+bool vim_iswordc_buf(int c, buf_T *buf)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2)
{
if (c >= 0x100) {
if (enc_dbcs != 0) {
@@ -840,10 +843,11 @@ int vim_iswordc_buf(int c, buf_T *buf)
/// Just like vim_iswordc() but uses a pointer to the (multi-byte) character.
///
-/// @param p
+/// @param p pointer to the multi-byte character
///
-/// @return TRUE if 'p' points to a keyword character.
-int vim_iswordp(char_u *p)
+/// @return true if "p" points to a keyword character.
+bool vim_iswordp(char_u *p)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (has_mbyte && (MB_BYTE2LEN(*p) > 1)) {
return mb_get_class(p) >= 2;
@@ -851,7 +855,15 @@ int vim_iswordp(char_u *p)
return GET_CHARTAB(curbuf, *p) != 0;
}
-int vim_iswordp_buf(char_u *p, buf_T *buf)
+/// Just like vim_iswordc_buf() but uses a pointer to the (multi-byte)
+/// character.
+///
+/// @param p pointer to the multi-byte character
+/// @param buf buffer whose keywords to use
+///
+/// @return true if "p" points to a keyword character.
+bool vim_iswordp_buf(char_u *p, buf_T *buf)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (has_mbyte && (MB_BYTE2LEN(*p) > 1)) {
return mb_get_class(p) >= 2;
@@ -859,26 +871,24 @@ int vim_iswordp_buf(char_u *p, buf_T *buf)
return GET_CHARTAB(buf, *p) != 0;
}
-/// return TRUE if 'c' is a valid file-name character
+/// Check that "c" is a valid file-name character.
/// Assume characters above 0x100 are valid (multi-byte).
///
-/// @param c
-///
-/// @return TRUE if 'c' is a valid file name character.
-int vim_isfilec(int c)
+/// @param c character to check
+bool vim_isfilec(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return c >= 0x100 || (c > 0 && (chartab[c] & CT_FNAME_CHAR));
}
-/// return TRUE if 'c' is a valid file-name character or a wildcard character
+/// Check that "c" is a valid file-name character or a wildcard character
/// Assume characters above 0x100 are valid (multi-byte).
/// Explicitly interpret ']' as a wildcard character as path_has_wildcard("]")
/// returns false.
///
-/// @param c
-///
-/// @return TRUE if 'c' is a valid file-name character or wildcard character.
-int vim_isfilec_or_wc(int c)
+/// @param c character to check
+bool vim_isfilec_or_wc(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
char_u buf[2];
buf[0] = (char_u)c;
@@ -886,14 +896,12 @@ int vim_isfilec_or_wc(int c)
return vim_isfilec(c) || c == ']' || path_has_wildcard(buf);
}
-/// return TRUE if 'c' is a printable character
-/// Assume characters above 0x100 are printable (multi-byte), except for
-/// Unicode.
-///
-/// @param c
+/// Check that "c" is a printable character.
+/// Assume characters above 0x100 are printable for double-byte encodings.
///
-/// @return TRUE if 'c' a printable character.
-int vim_isprintc(int c)
+/// @param c character to check
+bool vim_isprintc(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (enc_utf8 && (c >= 0x100)) {
return utf_printable(c);
@@ -901,16 +909,17 @@ int vim_isprintc(int c)
return c >= 0x100 || (c > 0 && (chartab[c] & CT_PRINT_CHAR));
}
-/// Strict version of vim_isprintc(c), don't return TRUE if "c" is the head
+/// Strict version of vim_isprintc(c), don't return true if "c" is the head
/// byte of a double-byte character.
///
-/// @param c
+/// @param c character to check
///
-/// @return TRUE if 'c' is a printable character.
-int vim_isprintc_strict(int c)
+/// @return true if "c" is a printable character.
+bool vim_isprintc_strict(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if ((enc_dbcs != 0) && (c < 0x100) && (MB_BYTE2LEN(c) > 1)) {
- return FALSE;
+ return false;
}
if (enc_utf8 && (c >= 0x100)) {
@@ -921,7 +930,7 @@ int vim_isprintc_strict(int c)
/// like chartabsize(), but also check for line breaks on the screen
///
-/// @param line
+/// @param line
/// @param s
/// @param col
///
@@ -1128,7 +1137,7 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
int n;
if ((*s == TAB) && (!wp->w_p_list || lcs_tab1)) {
- n = wp->w_buffer->b_p_ts;
+ n = (int)wp->w_buffer->b_p_ts;
return n - (col % n);
}
n = ptr2cells(s);
@@ -1144,35 +1153,33 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
return n;
}
-/// Return TRUE if virtual column "vcol" is in the rightmost column of window
-/// "wp".
+/// Check that virtual column "vcol" is in the rightmost column of window "wp".
///
-/// @param wp
-/// @param vcol
-///
-/// @return TRUE if the virtual column is in the rightmost column.
-int in_win_border(win_T *wp, colnr_T vcol)
+/// @param wp window
+/// @param vcol column number
+bool in_win_border(win_T *wp, colnr_T vcol)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
{
int width1; // width of first line (after line number)
int width2; // width of further lines
if (wp->w_width == 0) {
// there is no border
- return FALSE;
+ return false;
}
width1 = wp->w_width - win_col_off(wp);
if ((int)vcol < width1 - 1) {
- return FALSE;
+ return false;
}
if ((int)vcol == width1 - 1) {
- return TRUE;
+ return true;
}
width2 = width1 + win_col_off2(wp);
if (width2 <= 0) {
- return FALSE;
+ return false;
}
return (vcol - width1) % width2 == width2 - 1;
}
@@ -1198,11 +1205,11 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor,
char_u *line; // start of the line
int incr;
int head;
- int ts = wp->w_buffer->b_p_ts;
+ int ts = (int)wp->w_buffer->b_p_ts;
int c;
vcol = 0;
- line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE);
+ line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, false);
if (pos->col == MAXCOL) {
// continue until the NUL
@@ -1322,7 +1329,7 @@ colnr_T getvcol_nolist(pos_T *posp)
int list_save = curwin->w_p_list;
colnr_T vcol;
- curwin->w_p_list = FALSE;
+ curwin->w_p_list = false;
getvcol(curwin, posp, NULL, &vcol, NULL);
curwin->w_p_list = list_save;
return vcol;
@@ -1351,7 +1358,7 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor,
endadd = 0;
// Cannot put the cursor on part of a wide character.
- ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE);
+ ptr = ml_get_buf(wp->w_buffer, pos->lnum, false);
if (pos->col < (colnr_T)STRLEN(ptr)) {
int c = (*mb_ptr2char)(ptr + pos->col);
@@ -1571,10 +1578,14 @@ static char_u latin1lower[257] =
"\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee"
"\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff";
-int vim_islower(int c)
+/// Check that the character is lower-case
+///
+/// @param c character to check
+bool vim_islower(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (c <= '@') {
- return FALSE;
+ return false;
}
if (c >= 0x80) {
@@ -1584,11 +1595,11 @@ int vim_islower(int c)
if (c >= 0x100) {
if (has_mbyte) {
- return iswlower(c);
+ return iswlower((wint_t)c);
}
// islower() can't handle these chars and may crash
- return FALSE;
+ return false;
}
if (enc_latin1like) {
@@ -1598,10 +1609,14 @@ int vim_islower(int c)
return islower(c);
}
-int vim_isupper(int c)
+/// Check that the character is upper-case
+///
+/// @param c character to check
+bool vim_isupper(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (c <= '@') {
- return FALSE;
+ return false;
}
if (c >= 0x80) {
@@ -1611,11 +1626,11 @@ int vim_isupper(int c)
if (c >= 0x100) {
if (has_mbyte) {
- return iswupper(c);
+ return iswupper((wint_t)c);
}
- // islower() can't handle these chars and may crash
- return FALSE;
+ // isupper() can't handle these chars and may crash
+ return false;
}
if (enc_latin1like) {
@@ -1638,7 +1653,7 @@ int vim_toupper(int c)
if (c >= 0x100) {
if (has_mbyte) {
- return towupper(c);
+ return (int)towupper((wint_t)c);
}
// toupper() can't handle these chars and may crash
@@ -1665,7 +1680,7 @@ int vim_tolower(int c)
if (c >= 0x100) {
if (has_mbyte) {
- return towlower(c);
+ return (int)towlower((wint_t)c);
}
// tolower() can't handle these chars and may crash
@@ -1744,12 +1759,10 @@ long getdigits_long(char_u **pp)
return (long)number;
}
-/// Return TRUE if "lbuf" is empty or only contains blanks.
-///
-/// @param lbuf
+/// Check that "lbuf" is empty or only contains blanks.
///
-/// @return TRUE if `lbuf` is empty or only contains blanks.
-int vim_isblankline(char_u *lbuf)
+/// @param lbuf line buffer to check
+bool vim_isblankline(char_u *lbuf)
{
char_u *p = skipwhite(lbuf);
return *p == NUL || *p == '\r' || *p == '\n';
@@ -1781,10 +1794,11 @@ int vim_isblankline(char_u *lbuf)
/// @param nptr Returns the signed result.
/// @param unptr Returns the unsigned result.
/// @param maxlen Max length of string to check.
-void vim_str2nr(char_u *start, int *prep, int *len, int what,
- long *nptr, unsigned long *unptr, int maxlen)
+void vim_str2nr(const char_u *const start, int *const prep, int *const len,
+ const int what, long *const nptr, unsigned long *const unptr,
+ const int maxlen)
{
- char_u *ptr = start;
+ const char_u *ptr = start;
int pre = 0; // default is decimal
bool negative = false;
unsigned long un = 0;
@@ -1922,8 +1936,8 @@ int hex2nr(int c)
return c - '0';
}
-/// Return true if "str" starts with a backslash that should be removed.
-/// For WIN32 this is only done when the character after the
+/// Check that "str" starts with a backslash that should be removed.
+/// For Windows this is only done when the character after the
/// backslash is not a normal file name character.
/// '$' is a valid file name character, we don't remove the backslash before
/// it. This means it is not possible to use an environment variable after a
@@ -1934,10 +1948,9 @@ int hex2nr(int c)
/// character, assume that all multi-byte characters are valid file name
/// characters.
///
-/// @param str
-///
-/// @return true if `str` starts with a backslash that should be removed.
+/// @param str file path string to check
bool rem_backslash(const char_u *str)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
#ifdef BACKSLASH_IN_FILENAME
return str[0] == '\\'
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index ce79158050..4826e70727 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -29,7 +29,6 @@
#include "nvim/path.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
-#include "nvim/tempfile.h"
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
@@ -658,9 +657,9 @@ void ex_diffupdate(exarg_T *eap)
}
// We need three temp file names.
- char_u *tmp_orig = vim_tempname();
- char_u *tmp_new = vim_tempname();
- char_u *tmp_diff = vim_tempname();
+ char *tmp_orig = (char *) vim_tempname();
+ char *tmp_new = (char *) vim_tempname();
+ char *tmp_diff = (char *) vim_tempname();
if ((tmp_orig == NULL) || (tmp_new == NULL) || (tmp_diff == NULL)) {
goto theend;
@@ -670,11 +669,11 @@ void ex_diffupdate(exarg_T *eap)
// are no differences. Can't use the return value, it's non-zero when
// there are differences.
// May try twice, first with "-a" and then without.
- int io_error = FALSE;
- int ok = FALSE;
+ int io_error = false;
+ bool ok = false;
for (;;) {
- ok = FALSE;
- FILE *fd = mch_fopen((char *)tmp_orig, "w");
+ ok = false;
+ FILE *fd = mch_fopen(tmp_orig, "w");
if (fd == NULL) {
io_error = TRUE;
@@ -683,7 +682,7 @@ void ex_diffupdate(exarg_T *eap)
io_error = TRUE;
}
fclose(fd);
- fd = mch_fopen((char *)tmp_new, "w");
+ fd = mch_fopen(tmp_new, "w");
if (fd == NULL) {
io_error = TRUE;
@@ -693,7 +692,7 @@ void ex_diffupdate(exarg_T *eap)
}
fclose(fd);
diff_file(tmp_orig, tmp_new, tmp_diff);
- fd = mch_fopen((char *)tmp_diff, "r");
+ fd = mch_fopen(tmp_diff, "r");
if (fd == NULL) {
io_error = TRUE;
@@ -712,10 +711,10 @@ void ex_diffupdate(exarg_T *eap)
}
fclose(fd);
}
- os_remove((char *)tmp_diff);
- os_remove((char *)tmp_new);
+ os_remove(tmp_diff);
+ os_remove(tmp_new);
}
- os_remove((char *)tmp_orig);
+ os_remove(tmp_orig);
}
// When using 'diffexpr' break here.
@@ -756,28 +755,28 @@ void ex_diffupdate(exarg_T *eap)
// Write the first buffer to a tempfile.
buf_T *buf = curtab->tp_diffbuf[idx_orig];
- if (diff_write(buf, tmp_orig) == FAIL) {
+ if (diff_write(buf, (char_u *) tmp_orig) == FAIL) {
goto theend;
}
// Make a difference between the first buffer and every other.
for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) {
buf_T *buf = curtab->tp_diffbuf[idx_new];
- if (buf == NULL) {
- continue;
+ if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
+ continue; // skip buffer that isn't loaded
}
- if (diff_write(buf, tmp_new) == FAIL) {
+ if (diff_write(buf, (char_u *) tmp_new) == FAIL) {
continue;
}
diff_file(tmp_orig, tmp_new, tmp_diff);
// Read the diff output and add each entry to the diff list.
- diff_read(idx_orig, idx_new, tmp_diff);
- os_remove((char *)tmp_diff);
- os_remove((char *)tmp_new);
+ diff_read(idx_orig, idx_new, (char_u *) tmp_diff);
+ os_remove(tmp_diff);
+ os_remove(tmp_new);
}
- os_remove((char *)tmp_orig);
+ os_remove(tmp_orig);
// force updating cursor position on screen
curwin->w_valid_cursor.lnum = 0;
@@ -795,15 +794,16 @@ theend:
/// @param tmp_orig
/// @param tmp_new
/// @param tmp_diff
-static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff)
+static void diff_file(const char *const tmp_orig, const char *const tmp_new,
+ const char *const tmp_diff)
{
if (*p_dex != NUL) {
// Use 'diffexpr' to generate the diff file.
eval_diff(tmp_orig, tmp_new, tmp_diff);
} else {
- size_t len = STRLEN(tmp_orig) + STRLEN(tmp_new) + STRLEN(tmp_diff)
- + STRLEN(p_srr) + 27;
- char_u *cmd = xmalloc(len);
+ const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff)
+ + STRLEN(p_srr) + 27);
+ char *const cmd = xmalloc(len);
/* We don't want $DIFF_OPTIONS to get in the way. */
if (os_getenv("DIFF_OPTIONS")) {
@@ -813,19 +813,17 @@ static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff)
/* Build the diff command and execute it. Always use -a, binary
* differences are of no use. Ignore errors, diff returns
* non-zero when differences have been found. */
- vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s %s",
- diff_a_works == FALSE ? "" : "-a ",
+ vim_snprintf(cmd, len, "diff %s%s%s%s%s %s",
+ diff_a_works ? "-a " : "",
"",
(diff_flags & DIFF_IWHITE) ? "-b " : "",
(diff_flags & DIFF_ICASE) ? "-i " : "",
tmp_orig, tmp_new);
- append_redir(cmd, (int)len, p_srr, tmp_diff);
- block_autocmds(); /* Avoid ShellCmdPost stuff */
- (void)call_shell(
- cmd,
- kShellOptFilter | kShellOptSilent | kShellOptDoOut,
- NULL
- );
+ append_redir(cmd, len, (char *) p_srr, tmp_diff);
+ block_autocmds(); // Avoid ShellCmdPost stuff
+ (void)call_shell((char_u *) cmd,
+ kShellOptFilter | kShellOptSilent | kShellOptDoOut,
+ NULL);
unblock_autocmds();
xfree(cmd);
}
@@ -902,9 +900,11 @@ void ex_diffpatch(exarg_T *eap)
if (*p_pex != NUL) {
// Use 'patchexpr' to generate the new file.
#ifdef UNIX
- eval_patch(tmp_orig, fullname != NULL ? fullname : eap->arg, tmp_new);
+ eval_patch((char *) tmp_orig,
+ (char *) (fullname != NULL ? fullname : eap->arg),
+ (char *) tmp_new);
#else
- eval_patch(tmp_orig, eap->arg, tmp_new);
+ eval_patch((char *) tmp_orig, (char *) eap->arg, (char *) tmp_new);
#endif // ifdef UNIX
} else {
// Build the patch command and execute it. Ignore errors. Switch to
@@ -1057,27 +1057,28 @@ void diff_win_options(win_T *wp, int addbuf)
newFoldLevel();
curwin = old_curwin;
- wp->w_p_diff = TRUE;
-
// Use 'scrollbind' and 'cursorbind' when available
- if (!wp->w_p_diff_saved) {
+ if (!wp->w_p_diff) {
wp->w_p_scb_save = wp->w_p_scb;
}
wp->w_p_scb = TRUE;
- if (!wp->w_p_diff_saved) {
+ if (!wp->w_p_diff) {
wp->w_p_crb_save = wp->w_p_crb;
}
wp->w_p_crb = TRUE;
- if (!wp->w_p_diff_saved) {
+ if (!wp->w_p_diff) {
wp->w_p_wrap_save = wp->w_p_wrap;
}
wp->w_p_wrap = FALSE;
curwin = wp;
curbuf = curwin->w_buffer;
- if (!wp->w_p_diff_saved) {
+ if (!wp->w_p_diff) {
+ if (wp->w_p_diff_saved) {
+ free_string_option(wp->w_p_fdm_save);
+ }
wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm);
}
set_string_option_direct((char_u *)"fdm", -1, (char_u *)"diff",
@@ -1085,7 +1086,7 @@ void diff_win_options(win_T *wp, int addbuf)
curwin = old_curwin;
curbuf = curwin->w_buffer;
- if (!wp->w_p_diff_saved) {
+ if (!wp->w_p_diff) {
wp->w_p_fdc_save = wp->w_p_fdc;
wp->w_p_fen_save = wp->w_p_fen;
wp->w_p_fdl_save = wp->w_p_fdl;
@@ -1104,6 +1105,8 @@ void diff_win_options(win_T *wp, int addbuf)
// Saved the current values, to be restored in ex_diffoff().
wp->w_p_diff_saved = TRUE;
+ wp->w_p_diff = true;
+
if (addbuf) {
diff_buf_add(wp->w_buffer);
}
@@ -1116,68 +1119,50 @@ void diff_win_options(win_T *wp, int addbuf)
/// @param eap
void ex_diffoff(exarg_T *eap)
{
- win_T *old_curwin = curwin;
- int diffwin = FALSE;
+ int diffwin = false;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (eap->forceit ? wp->w_p_diff : (wp == curwin)) {
- // Set 'diff', 'scrollbind' off and 'wrap' on. If option values
- // were saved in diff_win_options() restore them.
- wp->w_p_diff = FALSE;
-
- if (wp->w_p_scb) {
- wp->w_p_scb = wp->w_p_diff_saved ? wp->w_p_scb_save : FALSE;
- }
-
- if (wp->w_p_crb) {
- wp->w_p_crb = wp->w_p_diff_saved ? wp->w_p_crb_save : FALSE;
- }
-
- if (!wp->w_p_wrap) {
- wp->w_p_wrap = wp->w_p_diff_saved ? wp->w_p_wrap_save : TRUE;
- }
- curwin = wp;
- curbuf = curwin->w_buffer;
+ // Set 'diff' off. If option values were saved in
+ // diff_win_options(), restore the ones whose settings seem to have
+ // been left over from diff mode.
+ wp->w_p_diff = false;
if (wp->w_p_diff_saved) {
- free_string_option(wp->w_p_fdm);
- wp->w_p_fdm = wp->w_p_fdm_save;
- wp->w_p_fdm_save = empty_option;
- } else {
- set_string_option_direct((char_u *)"fdm", -1,
- (char_u *)"manual", OPT_LOCAL | OPT_FREE, 0);
- }
- curwin = old_curwin;
- curbuf = curwin->w_buffer;
+ if (wp->w_p_scb) {
+ wp->w_p_scb = wp->w_p_scb_save;
+ }
- if (wp->w_p_fdc == diff_foldcolumn) {
- wp->w_p_fdc = wp->w_p_diff_saved ? wp->w_p_fdc_save : 0;
- }
+ if (wp->w_p_crb) {
+ wp->w_p_crb = wp->w_p_crb_save;
+ }
- if ((wp->w_p_fdl == 0)
- && wp->w_p_diff_saved) {
- wp->w_p_fdl = wp->w_p_fdl_save;
- }
+ if (!wp->w_p_wrap) {
+ wp->w_p_wrap = wp->w_p_wrap_save;
+ }
- if (wp->w_p_fen) {
+ free_string_option(wp->w_p_fdm);
+ wp->w_p_fdm = vim_strsave(wp->w_p_fdm_save);
+ if (wp->w_p_fdc == diff_foldcolumn) {
+ wp->w_p_fdc = wp->w_p_fdc_save;
+ }
+ if (wp->w_p_fdl == 0) {
+ wp->w_p_fdl = wp->w_p_fdl_save;
+ }
// Only restore 'foldenable' when 'foldmethod' is not
// "manual", otherwise we continue to show the diff folds.
- if (foldmethodIsManual(wp) || !wp->w_p_diff_saved) {
- wp->w_p_fen = FALSE;
- } else {
- wp->w_p_fen = wp->w_p_fen_save;
+ if (wp->w_p_fen) {
+ wp->w_p_fen = foldmethodIsManual(wp) ? false : wp->w_p_fen_save;
}
- }
- foldUpdateAll(wp);
+ foldUpdateAll(wp);
- // make sure topline is not halfway through a fold
- changed_window_setting_win(wp);
+ // make sure topline is not halfway through a fold
+ changed_window_setting_win(wp);
+ }
// Note: 'sbo' is not restored, it's a global option.
diff_buf_adjust(wp);
-
- wp->w_p_diff_saved = FALSE;
}
diffwin |= wp->w_p_diff;
}
@@ -1546,37 +1531,37 @@ int diff_check(win_T *wp, linenr_T lnum)
return maxcount - dp->df_count[idx];
}
-/// Compare two entries in diff "*dp" and return TRUE if they are equal.
+/// Compare two entries in diff "dp" and return true if they are equal.
///
-/// @param dp
-/// @param idx1 First entry in diff "*dp"
-/// @param idx2 Second entry in diff "*dp"
+/// @param dp diff
+/// @param idx1 first entry in diff "dp"
+/// @param idx2 second entry in diff "dp"
///
-/// @return return TRUE if two entires are equal.
-static int diff_equal_entry(diff_T *dp, int idx1, int idx2)
+/// @return true if two entires are equal.
+static bool diff_equal_entry(diff_T *dp, int idx1, int idx2)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
{
if (dp->df_count[idx1] != dp->df_count[idx2]) {
- return FALSE;
+ return false;
}
if (diff_check_sanity(curtab, dp) == FAIL) {
- return FALSE;
+ return false;
}
- int i;
- for (i = 0; i < dp->df_count[idx1]; ++i) {
+ for (int i = 0; i < dp->df_count[idx1]; i++) {
char_u *line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1],
- dp->df_lnum[idx1] + i, FALSE));
+ dp->df_lnum[idx1] + i, false));
int cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2],
- dp->df_lnum[idx2] + i, FALSE));
+ dp->df_lnum[idx2] + i, false));
xfree(line);
if (cmp != 0) {
- return FALSE;
+ return false;
}
}
- return TRUE;
+ return true;
}
/// Compare strings "s1" and "s2" according to 'diffopt'.
@@ -1845,28 +1830,30 @@ int diffopt_changed(void)
return OK;
}
-/// Return TRUE if 'diffopt' contains "horizontal".
-///
-/// @return TRUE if 'diffopt' contains "horizontal"
-int diffopt_horizontal(void)
+/// Check that "diffopt" contains "horizontal".
+bool diffopt_horizontal(void)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return (diff_flags & DIFF_HORIZONTAL) != 0;
}
/// Find the difference within a changed line.
///
-/// @param startp first char of the change
-/// @param endp last char of the change
+/// @param wp window whose current buffer to check
+/// @param lnum line number to check within the buffer
+/// @param startp first char of the change
+/// @param endp last char of the change
///
-/// @returns TRUE if the line was added, no other buffer has it.
-int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
+/// @return true if the line was added, no other buffer has it.
+bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
char_u *line_new;
int si_org;
int si_new;
int ei_org;
int ei_new;
- int added = TRUE;
+ bool added = true;
// Make a copy of the line, the next ml_get() will invalidate it.
char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE));
@@ -1875,7 +1862,7 @@ int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
if (idx == DB_COUNT) {
// cannot happen
xfree(line_org);
- return FALSE;
+ return false;
}
// search for a change that includes "lnum" in the list of diffblocks.
@@ -1888,7 +1875,7 @@ int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
if ((dp == NULL) || (diff_check_sanity(curtab, dp) == FAIL)) {
xfree(line_org);
- return FALSE;
+ return false;
}
int off = lnum - dp->df_lnum[idx];
@@ -1899,7 +1886,7 @@ int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
if (off >= dp->df_count[i]) {
continue;
}
- added = FALSE;
+ added = false;
line_new = ml_get_buf(curtab->tp_diffbuf[i],
dp->df_lnum[i] + off, FALSE);
@@ -1971,21 +1958,22 @@ int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
return added;
}
-/// Return TRUE if line "lnum" is not close to a diff block, this line should
+/// Check that line "lnum" is not close to a diff block, this line should
/// be in a fold.
///
-/// @param wp
-/// @param lnum
+/// @param wp window containing the buffer to check
+/// @param lnum line number to check within the buffer
///
-/// @return FALSE if there are no diff blocks at all in this window.
-int diff_infold(win_T *wp, linenr_T lnum)
+/// @return false if there are no diff blocks at all in this window.
+bool diff_infold(win_T *wp, linenr_T lnum)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
{
- int other = FALSE;
+ bool other = false;
diff_T *dp;
// Return if 'diff' isn't set.
if (!wp->w_p_diff) {
- return FALSE;
+ return false;
}
int idx = -1;
@@ -1994,13 +1982,13 @@ int diff_infold(win_T *wp, linenr_T lnum)
if (curtab->tp_diffbuf[i] == wp->w_buffer) {
idx = i;
} else if (curtab->tp_diffbuf[i] != NULL) {
- other = TRUE;
+ other = true;
}
}
// return here if there are no diffs in the window
if ((idx == -1) || !other) {
- return FALSE;
+ return false;
}
if (curtab->tp_diff_invalid) {
@@ -2010,7 +1998,7 @@ int diff_infold(win_T *wp, linenr_T lnum)
// Return if there are no diff blocks. All lines will be folded.
if (curtab->tp_first_diff == NULL) {
- return TRUE;
+ return true;
}
for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
@@ -2021,10 +2009,10 @@ int diff_infold(win_T *wp, linenr_T lnum)
// If this change ends before the line we have a match.
if (dp->df_lnum[idx] + dp->df_count[idx] + diff_context > lnum) {
- return FALSE;
+ return false;
}
}
- return TRUE;
+ return true;
}
/// "dp" and "do" commands.
@@ -2387,12 +2375,11 @@ static void diff_fold_update(diff_T *dp, int skip_idx)
}
}
-/// Checks if the buffer is in diff-mode.
-///
-/// @param buf The buffer to check.
+/// Checks that the buffer is in diff-mode.
///
-/// @return TRUE if buffer "buf" is in diff-mode.
+/// @param buf buffer to check.
bool diff_mode_buf(buf_T *buf)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
{
FOR_ALL_TABS(tp) {
if (diff_buf_idx_tp(buf, tp) != DB_COUNT) {
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 213df4f65a..e131da8fe0 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -165,10 +165,6 @@ static int compl_restarting = FALSE; /* don't insert match */
* FALSE the word to be completed must be located. */
static int compl_started = FALSE;
-/* Set when doing something for completion that may call edit() recursively,
- * which is not allowed. */
-static int compl_busy = FALSE;
-
static int compl_matches = 0;
static char_u *compl_pattern = NULL;
static int compl_direction = FORWARD;
@@ -203,7 +199,7 @@ typedef struct insert_state {
int did_restart_edit; // remember if insert mode was restarted
// after a ctrl+o
bool nomove;
- uint8_t *ptr;
+ char_u *ptr;
} InsertState;
@@ -274,8 +270,8 @@ static void insert_enter(InsertState *s)
s->ptr = (char_u *)"i";
}
- set_vim_var_string(VV_INSERTMODE, s->ptr, 1);
- set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */
+ set_vim_var_string(VV_INSERTMODE, (char *) s->ptr, 1);
+ set_vim_var_string(VV_CHAR, NULL, -1);
apply_autocmds(EVENT_INSERTENTER, NULL, NULL, false, curbuf);
// Make sure the cursor didn't move. Do call check_cursor_col() in
@@ -887,7 +883,7 @@ static int insert_handle_key(InsertState *s)
case Ctrl_T: // Make indent one shiftwidth greater.
if (s->c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) {
- if (has_compl_option(false)) {
+ if (check_compl_option(false)) {
insert_do_complete(s);
}
break;
@@ -1102,7 +1098,7 @@ static int insert_handle_key(InsertState *s)
case Ctrl_K: // digraph or keyword completion
if (ctrl_x_mode == CTRL_X_DICTIONARY) {
- if (has_compl_option(true)) {
+ if (check_compl_option(true)) {
insert_do_complete(s);
}
break;
@@ -1247,7 +1243,7 @@ normalchar:
static void insert_do_complete(InsertState *s)
{
compl_busy = true;
- if (ins_complete(s->c) == FAIL) {
+ if (ins_complete(s->c, true) == FAIL) {
compl_cont_status = 0;
}
compl_busy = false;
@@ -1264,30 +1260,28 @@ static void insert_do_cindent(InsertState *s)
}
}
-/*
- * edit(): Start inserting text.
- *
- * "cmdchar" can be:
- * 'i' normal insert command
- * 'a' normal append command
- * 'R' replace command
- * 'r' "r<CR>" command: insert one <CR>. Note: count can be > 1, for redo,
- * but still only one <CR> is inserted. The <Esc> is not used for redo.
- * 'g' "gI" command.
- * 'V' "gR" command for Virtual Replace mode.
- * 'v' "gr" command for single character Virtual Replace mode.
- *
- * This function is not called recursively. For CTRL-O commands, it returns
- * and lets the caller handle the Normal-mode command.
- *
- * Return TRUE if a CTRL-O command caused the return (insert mode pending).
- */
-int
-edit (
- int cmdchar,
- int startln, /* if set, insert at start of line */
- long count
-)
+/// edit(): Start inserting text.
+///
+/// "cmdchar" can be:
+/// 'i' normal insert command
+/// 'a' normal append command
+/// 'R' replace command
+/// 'r' "r<CR>" command: insert one <CR>.
+/// Note: count can be > 1, for redo, but still only one <CR> is inserted.
+/// <Esc> is not used for redo.
+/// 'g' "gI" command.
+/// 'V' "gR" command for Virtual Replace mode.
+/// 'v' "gr" command for single character Virtual Replace mode.
+///
+/// This function is not called recursively. For CTRL-O commands, it returns
+/// and lets the caller handle the Normal-mode command.
+///
+/// @param cmdchar command that started the insert
+/// @param startln if true, insert at start of line
+/// @param count repeat count for the command
+///
+/// @return true if a CTRL-O command caused the return (insert mode pending).
+bool edit(int cmdchar, bool startln, long count)
{
if (curbuf->terminal) {
if (ex_normal_busy) {
@@ -1365,6 +1359,9 @@ ins_redraw (
update_screen(0);
}
if (has_event(EVENT_CURSORMOVEDI)) {
+ // Make sure curswant is correct, an autocommand may call
+ // getcurpos()
+ update_curswant();
apply_autocmds(EVENT_CURSORMOVEDI, NULL, NULL, false, curbuf);
}
if (curwin->w_p_cole > 0) {
@@ -1795,33 +1792,37 @@ void backspace_until_column(int col)
}
}
-/*
- * Like del_char(), but make sure not to go before column "limit_col".
- * Only matters when there are composing characters.
- * Return TRUE when something was deleted.
- */
-static int del_char_after_col(int limit_col)
+/// Like del_char(), but make sure not to go before column "limit_col".
+/// Only matters when there are composing characters.
+///
+/// @param limit_col only delete the character if it is after this column
+//
+/// @return true when something was deleted.
+static bool del_char_after_col(int limit_col)
{
if (enc_utf8 && limit_col >= 0) {
colnr_T ecol = curwin->w_cursor.col + 1;
- /* Make sure the cursor is at the start of a character, but
- * skip forward again when going too far back because of a
- * composing character. */
+ // Make sure the cursor is at the start of a character, but
+ // skip forward again when going too far back because of a
+ // composing character.
mb_adjust_cursor();
while (curwin->w_cursor.col < (colnr_T)limit_col) {
int l = utf_ptr2len(get_cursor_pos_ptr());
- if (l == 0) /* end of line */
+ if (l == 0) { // end of line
break;
+ }
curwin->w_cursor.col += l;
}
- if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol)
- return FALSE;
- del_bytes((long)((int)ecol - curwin->w_cursor.col), FALSE, TRUE);
- } else
- (void)del_char(FALSE);
- return TRUE;
+ if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol) {
+ return false;
+ }
+ del_bytes(ecol - curwin->w_cursor.col, false, true);
+ } else {
+ del_char(false);
+ }
+ return true;
}
/*
@@ -1846,47 +1847,48 @@ static void ins_ctrl_x(void)
}
}
-/*
- * Return TRUE if the 'dict' or 'tsr' option can be used.
- */
-static int has_compl_option(int dict_opt)
+/// Check that the "dict" or "tsr" option can be used.
+///
+/// @param dict_opt check "dict" when true, "tsr" when false.
+static bool check_compl_option(bool dict_opt)
{
- if (dict_opt ? (*curbuf->b_p_dict == NUL && *p_dict == NUL
- && !curwin->w_p_spell
- )
+ if (dict_opt
+ ? (*curbuf->b_p_dict == NUL && *p_dict == NUL && !curwin->w_p_spell)
: (*curbuf->b_p_tsr == NUL && *p_tsr == NUL)) {
ctrl_x_mode = 0;
edit_submode = NULL;
msg_attr(dict_opt ? (char_u *)_("'dictionary' option is empty")
- : (char_u *)_("'thesaurus' option is empty"),
- hl_attr(HLF_E));
+ : (char_u *)_("'thesaurus' option is empty"), hl_attr(HLF_E));
if (emsg_silent == 0) {
vim_beep(BO_COMPL);
setcursor();
ui_flush();
os_delay(2000L, false);
}
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
-/*
- * Is the character 'c' a valid key to go to or keep us in CTRL-X mode?
- * This depends on the current mode.
- */
-int vim_is_ctrl_x_key(int c)
+/// Check that the character "c" a valid key to go to or keep us in CTRL-X mode?
+/// This depends on the current mode.
+///
+/// @param c character to check
+bool vim_is_ctrl_x_key(int c)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
- /* Always allow ^R - let it's results then be checked */
- if (c == Ctrl_R)
- return TRUE;
+ // Always allow ^R - let its results then be checked
+ if (c == Ctrl_R) {
+ return true;
+ }
- /* Accept <PageUp> and <PageDown> if the popup menu is visible. */
- if (ins_compl_pum_key(c))
- return TRUE;
+ // Accept <PageUp> and <PageDown> if the popup menu is visible.
+ if (ins_compl_pum_key(c)) {
+ return true;
+ }
switch (ctrl_x_mode) {
- case 0: /* Not in any CTRL-X mode */
+ case 0: // Not in any CTRL-X mode
return c == Ctrl_N || c == Ctrl_P || c == Ctrl_X;
case CTRL_X_NOT_DEFINED_YET:
return c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E
@@ -1924,35 +1926,37 @@ int vim_is_ctrl_x_key(int c)
return (c == Ctrl_P || c == Ctrl_N);
}
EMSG(_(e_internal));
- return FALSE;
+ return false;
}
-/*
- * Return TRUE when character "c" is part of the item currently being
- * completed. Used to decide whether to abandon complete mode when the menu
- * is visible.
- */
-static int ins_compl_accept_char(int c)
+/// Check that character "c" is part of the item currently being
+/// completed. Used to decide whether to abandon complete mode when the menu
+/// is visible.
+///
+/// @param c character to check
+static bool ins_compl_accept_char(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- if (ctrl_x_mode & CTRL_X_WANT_IDENT)
- /* When expanding an identifier only accept identifier chars. */
+ if (ctrl_x_mode & CTRL_X_WANT_IDENT) {
+ // When expanding an identifier only accept identifier chars.
return vim_isIDc(c);
+ }
switch (ctrl_x_mode) {
case CTRL_X_FILES:
- /* When expanding file name only accept file name chars. But not
- * path separators, so that "proto/<Tab>" expands files in
- * "proto", not "proto/" as a whole */
+ // When expanding file name only accept file name chars. But not
+ // path separators, so that "proto/<Tab>" expands files in
+ // "proto", not "proto/" as a whole
return vim_isfilec(c) && !vim_ispathsep(c);
case CTRL_X_CMDLINE:
case CTRL_X_OMNI:
- /* Command line and Omni completion can work with just about any
- * printable character, but do stop at white space. */
+ // Command line and Omni completion can work with just about any
+ // printable character, but do stop at white space.
return vim_isprintc(c) && !ascii_iswhite(c);
case CTRL_X_WHOLE_LINE:
- /* For while line completion a space can be part of the line. */
+ // For while line completion a space can be part of the line.
return vim_isprintc(c);
}
return vim_iswordc(c);
@@ -2197,15 +2201,19 @@ ins_compl_add (
return OK;
}
-/*
- * Return TRUE if "str[len]" matches with match->cp_str, considering
- * match->cp_icase.
- */
-static int ins_compl_equal(compl_T *match, char_u *str, int len)
+/// Check that "str[len]" matches with "match->cp_str", considering
+/// "match->cp_icase".
+///
+/// @param match completion match
+/// @param str character string to check
+/// @param len lenth of "str"
+static bool ins_compl_equal(compl_T *match, char_u *str, size_t len)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- if (match->cp_icase)
- return STRNICMP(match->cp_str, str, (size_t)len) == 0;
- return STRNCMP(match->cp_str, str, (size_t)len) == 0;
+ if (match->cp_icase) {
+ return STRNICMP(match->cp_str, str, len) == 0;
+ }
+ return STRNCMP(match->cp_str, str, len) == 0;
}
/*
@@ -2314,6 +2322,22 @@ static int ins_compl_make_cyclic(void)
return count;
}
+
+// Set variables that store noselect and noinsert behavior from the
+// 'completeopt' value.
+void completeopt_was_set(void)
+{
+ compl_no_insert = false;
+ compl_no_select = false;
+ if (strstr((char *)p_cot, "noselect") != NULL) {
+ compl_no_select = true;
+ }
+ if (strstr((char *)p_cot, "noinsert") != NULL) {
+ compl_no_insert = true;
+ }
+}
+
+
/*
* Start completion for the complete() function.
* "startcol" is where the matched text starts (1 is first column).
@@ -2321,9 +2345,10 @@ static int ins_compl_make_cyclic(void)
*/
void set_completion(colnr_T startcol, list_T *list)
{
- /* If already doing completions stop it. */
- if (ctrl_x_mode != 0)
+ // If already doing completions stop it.
+ if (ctrl_x_mode != 0) {
ins_compl_prep(' ');
+ }
ins_compl_clear();
if (stop_arrow() == FAIL)
@@ -2349,16 +2374,23 @@ void set_completion(colnr_T startcol, list_T *list)
compl_started = TRUE;
compl_used_match = TRUE;
compl_cont_status = 0;
+ int save_w_wrow = curwin->w_wrow;
compl_curr_match = compl_first_match;
- if (compl_no_insert) {
- ins_complete(K_DOWN);
- } else {
- ins_complete(Ctrl_N);
+ if (compl_no_insert || compl_no_select) {
+ ins_complete(K_DOWN, false);
if (compl_no_select) {
- ins_complete(Ctrl_P);
+ ins_complete(K_UP, false);
}
+ } else {
+ ins_complete(Ctrl_N, false);
}
+
+ // Lazily show the popup menu, unless we got interrupted.
+ if (!compl_interrupted) {
+ show_pum(save_w_wrow);
+ }
+
ui_flush();
}
@@ -2395,44 +2427,33 @@ static void ins_compl_del_pum(void)
}
}
-/*
- * Return TRUE if the popup menu should be displayed.
- */
-static int pum_wanted(void)
+/// Check if the popup menu should be displayed.
+static bool pum_wanted(void)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- /* 'completeopt' must contain "menu" or "menuone" */
- if (vim_strchr(p_cot, 'm') == NULL)
- return FALSE;
-
- /* The display looks bad on a B&W display. */
- if (t_colors < 8
- )
- return FALSE;
- return TRUE;
+ // "completeopt" must contain "menu" or "menuone"
+ return vim_strchr(p_cot, 'm') != NULL;
}
-/*
- * Return TRUE if there are two or more matches to be shown in the popup menu.
- * One if 'completopt' contains "menuone".
- */
-static int pum_enough_matches(void)
+/// Check that there are two or more matches to be shown in the popup menu.
+/// One if "completopt" contains "menuone".
+static bool pum_enough_matches(void)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- compl_T *compl;
- int i;
-
- /* Don't display the popup menu if there are no matches or there is only
- * one (ignoring the original text). */
- compl = compl_first_match;
- i = 0;
+ // Don't display the popup menu if there are no matches or there is only
+ // one (ignoring the original text).
+ compl_T *comp = compl_first_match;
+ int i = 0;
do {
- if (compl == NULL
- || ((compl->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2))
+ if (comp == NULL || ((comp->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) {
break;
- compl = compl->cp_next;
- } while (compl != compl_first_match);
+ }
+ comp = comp->cp_next;
+ } while (comp != compl_first_match);
- if (strstr((char *)p_cot, "menuone") != NULL)
+ if (strstr((char *)p_cot, "menuone") != NULL) {
return i >= 1;
+ }
return i >= 2;
}
@@ -2464,6 +2485,14 @@ void ins_compl_show_pum(void)
/* Need to build the popup menu list. */
compl_match_arraysize = 0;
compl = compl_first_match;
+ /*
+ * If it's user complete function and refresh_always,
+ * not use "compl_leader" as prefix filter.
+ */
+ if (ins_compl_need_restart()){
+ xfree(compl_leader);
+ compl_leader = NULL;
+ }
if (compl_leader != NULL)
lead_len = (int)STRLEN(compl_leader);
do {
@@ -2855,10 +2884,9 @@ static void ins_compl_clear(void)
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
}
-/*
- * Return TRUE when Insert completion is active.
- */
-int ins_compl_active(void)
+/// Check that Insert completion is active.
+bool ins_compl_active(void)
+ FUNC_ATTR_PURE
{
return compl_started;
}
@@ -2902,14 +2930,13 @@ static int ins_compl_bs(void)
return NUL;
}
-/*
- * Return TRUE when we need to find matches again, ins_compl_restart() is to
- * be called.
- */
-static int ins_compl_need_restart(void)
+/// Check that we need to find matches again, ins_compl_restart() is to
+/// be called.
+static bool ins_compl_need_restart(void)
+ FUNC_ATTR_PURE
{
- /* Return TRUE if we didn't complete finding matches or when the
- * 'completefunc' returned "always" in the "refresh" dictionary item. */
+ // Return true if we didn't complete finding matches or when the
+ // "completefunc" returned "always" in the "refresh" dictionary item.
return compl_was_interrupted
|| ((ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI)
&& compl_opt_refresh_always);
@@ -2927,20 +2954,17 @@ static void ins_compl_new_leader(void)
ins_bytes(compl_leader + ins_compl_len());
compl_used_match = FALSE;
- if (compl_started)
+ if (compl_started) {
ins_compl_set_original_text(compl_leader);
- else {
- spell_bad_len = 0; /* need to redetect bad word */
- /*
- * Matches were cleared, need to search for them now. First display
- * the changed text before the cursor. Set "compl_restarting" to
- * avoid that the first match is inserted.
- */
- update_screen(0);
- compl_restarting = TRUE;
- if (ins_complete(Ctrl_N) == FAIL)
+ } else {
+ spell_bad_len = 0; // need to redetect bad word
+ // Matches were cleared, need to search for them now.
+ // Set "compl_restarting" to avoid that the first match is inserted.
+ compl_restarting = true;
+ if (ins_complete(Ctrl_N, true) == FAIL) {
compl_cont_status = 0;
- compl_restarting = FALSE;
+ }
+ compl_restarting = false;
}
compl_enter_selects = !compl_used_match;
@@ -2948,8 +2972,9 @@ static void ins_compl_new_leader(void)
/* Show the popup menu with a different set of matches. */
ins_compl_show_pum();
- /* Don't let Enter select the original text when there is no popup menu. */
- if (compl_match_array == NULL)
+ /* Don't let Enter select the original text when there is no popup menu.
+ * Don't let Enter select when use user function and refresh_always is set */
+ if (compl_match_array == NULL || ins_compl_need_restart())
compl_enter_selects = FALSE;
}
@@ -2980,27 +3005,18 @@ static void ins_compl_addleader(int c)
(*mb_char2bytes)(c, buf);
buf[cc] = NUL;
ins_char_bytes(buf, cc);
- if (compl_opt_refresh_always)
- AppendToRedobuff(buf);
} else {
ins_char(c);
- if (compl_opt_refresh_always)
- AppendCharToRedobuff(c);
}
/* If we didn't complete finding matches we must search again. */
if (ins_compl_need_restart())
ins_compl_restart();
- /* When 'always' is set, don't reset compl_leader. While completing,
- * cursor doesn't point original position, changing compl_leader would
- * break redo. */
- if (!compl_opt_refresh_always) {
- xfree(compl_leader);
- compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col,
- (int)(curwin->w_cursor.col - compl_col));
- ins_compl_new_leader();
- }
+ xfree(compl_leader);
+ compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col,
+ (int)(curwin->w_cursor.col - compl_col));
+ ins_compl_new_leader();
}
/*
@@ -3009,6 +3025,10 @@ static void ins_compl_addleader(int c)
*/
static void ins_compl_restart(void)
{
+ /* update screen before restart.
+ * so if complete is blocked,
+ * will stay to the last popup menu and reduce flicker */
+ update_screen(0);
ins_compl_free();
compl_started = FALSE;
compl_matches = 0;
@@ -3064,16 +3084,16 @@ static void ins_compl_addfrommatch(void)
ins_compl_addleader(c);
}
-/*
- * Prepare for Insert mode completion, or stop it.
- * Called just after typing a character in Insert mode.
- * Returns TRUE when the character is not to be inserted;
- */
-static int ins_compl_prep(int c)
+/// Prepare for Insert mode completion, or stop it.
+/// Called just after typing a character in Insert mode.
+///
+/// @param c character that was typed
+///
+/// @return true when the character is not to be inserted;
+static bool ins_compl_prep(int c)
{
- char_u *ptr;
- int want_cindent;
- int retval = FALSE;
+ char_u *ptr;
+ bool retval = false;
/* Forget any previous 'special' messages if this is actually
* a ^X mode key - bar ^R, in which case we wait to see what it gives us.
@@ -3083,8 +3103,10 @@ static int ins_compl_prep(int c)
/* Ignore end of Select mode mapping and mouse scroll buttons. */
if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP
- || c == K_MOUSELEFT || c == K_MOUSERIGHT)
+ || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT
+ || c == K_FOCUSGAINED || c == K_FOCUSLOST) {
return retval;
+ }
/* Set "compl_get_longest" when finding the first matches. */
if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET
@@ -3094,17 +3116,6 @@ static int ins_compl_prep(int c)
}
- if (strstr((char *)p_cot, "noselect") != NULL) {
- compl_no_insert = FALSE;
- compl_no_select = TRUE;
- } else if (strstr((char *)p_cot, "noinsert") != NULL) {
- compl_no_insert = TRUE;
- compl_no_select = FALSE;
- } else {
- compl_no_insert = FALSE;
- compl_no_select = FALSE;
- }
-
if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET) {
/*
* We have just typed CTRL-X and aren't quite sure which CTRL-X mode
@@ -3238,11 +3249,9 @@ static int ins_compl_prep(int c)
ins_compl_fixRedoBufForLeader(ptr);
}
- want_cindent = (can_cindent && cindent_on());
- /*
- * When completing whole lines: fix indent for 'cindent'.
- * Otherwise, break line if it's too long.
- */
+ bool want_cindent = (can_cindent && cindent_on());
+ // When completing whole lines: fix indent for 'cindent'.
+ // Otherwise, break line if it's too long.
if (compl_cont_mode == CTRL_X_WHOLE_LINE) {
/* re-indent the current line */
if (want_cindent) {
@@ -3262,22 +3271,24 @@ static int ins_compl_prep(int c)
inc_cursor();
}
- /* If the popup menu is displayed pressing CTRL-Y means accepting
- * the selection without inserting anything. When
- * compl_enter_selects is set the Enter key does the same. */
+ // If the popup menu is displayed pressing CTRL-Y means accepting
+ // the selection without inserting anything. When
+ // compl_enter_selects is set the Enter key does the same.
if ((c == Ctrl_Y || (compl_enter_selects
&& (c == CAR || c == K_KENTER || c == NL)))
- && pum_visible())
- retval = TRUE;
+ && pum_visible()) {
+ retval = true;
+ }
/* CTRL-E means completion is Ended, go back to the typed text. */
if (c == Ctrl_E) {
ins_compl_delete();
- if (compl_leader != NULL)
+ if (compl_leader != NULL) {
ins_bytes(compl_leader + ins_compl_len());
- else if (compl_first_match != NULL)
+ } else if (compl_first_match != NULL) {
ins_bytes(compl_orig_text + ins_compl_len());
- retval = TRUE;
+ }
+ retval = true;
}
auto_format(FALSE, TRUE);
@@ -3295,6 +3306,12 @@ static int ins_compl_prep(int c)
showmode();
}
+ // Avoid the popup menu remains displayed when leaving the
+ // command line window.
+ if (c == Ctrl_C && cmdwin_type != 0) {
+ update_screen(0);
+ }
+
/*
* Indent now if a key was typed that is in 'cinkeys'.
*/
@@ -4236,22 +4253,22 @@ void ins_compl_check_keys(int frequency)
static int ins_compl_key2dir(int c)
{
if (c == Ctrl_P || c == Ctrl_L
- || (pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP
- || c == K_S_UP || c == K_UP)))
+ || c == K_PAGEUP || c == K_KPAGEUP
+ || c == K_S_UP || c == K_UP) {
return BACKWARD;
+ }
return FORWARD;
}
-/*
- * Return TRUE for keys that are used for completion only when the popup menu
- * is visible.
- */
-static int ins_compl_pum_key(int c)
+/// Check that "c" is a valid completion key only while the popup menu is shown
+///
+/// @param c character to check
+static bool ins_compl_pum_key(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP
- || c == K_PAGEDOWN || c == K_KPAGEDOWN || c ==
- K_S_DOWN
- || c == K_UP || c == K_DOWN);
+ || c == K_PAGEDOWN || c == K_KPAGEDOWN
+ || c == K_S_DOWN || c == K_UP || c == K_DOWN);
}
/*
@@ -4271,11 +4288,12 @@ static int ins_compl_key2count(int c)
return 1;
}
-/*
- * Return TRUE if completion with "c" should insert the match, FALSE if only
- * to change the currently selected completion.
- */
-static int ins_compl_use_match(int c)
+/// Check that completion with "c" should insert the match, false if only
+/// to change the currently selected completion.
+///
+/// @param c character to check
+static bool ins_compl_use_match(int c)
+ FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
{
switch (c) {
case K_UP:
@@ -4286,9 +4304,9 @@ static int ins_compl_use_match(int c)
case K_PAGEUP:
case K_KPAGEUP:
case K_S_UP:
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
/*
@@ -4296,7 +4314,7 @@ static int ins_compl_use_match(int c)
* Called when character "c" was typed, which has a meaning for completion.
* Returns OK if completion was done, FAIL if something failed.
*/
-static int ins_complete(int c)
+static int ins_complete(int c, bool enable_pum)
{
char_u *line;
int startcol = 0; /* column where searched text starts */
@@ -4412,11 +4430,10 @@ static int ins_complete(int c)
prefix = (char_u *)"";
STRCPY((char *)compl_pattern, prefix);
(void)quote_meta(compl_pattern + STRLEN(prefix),
- line + compl_col, compl_length);
- } else if (--startcol < 0 ||
- !vim_iswordp(mb_prevptr(line, line + startcol + 1))
- ) {
- /* Match any word of at least two chars */
+ line + compl_col, compl_length);
+ } else if (--startcol < 0
+ || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) {
+ // Match any word of at least two chars
compl_pattern = vim_strsave((char_u *)"\\<\\k\\k");
compl_col += curs_col;
compl_length = 0;
@@ -4779,20 +4796,9 @@ static int ins_complete(int c)
}
}
- /* Show the popup menu, unless we got interrupted. */
- if (!compl_interrupted) {
- /* RedrawingDisabled may be set when invoked through complete(). */
- n = RedrawingDisabled;
- RedrawingDisabled = 0;
-
- /* If the cursor moved we need to remove the pum first. */
- setcursor();
- if (save_w_wrow != curwin->w_wrow)
- ins_compl_del_pum();
-
- ins_compl_show_pum();
- setcursor();
- RedrawingDisabled = n;
+ // Show the popup menu, unless we got interrupted.
+ if (enable_pum && !compl_interrupted) {
+ show_pum(save_w_wrow);
}
compl_was_interrupted = compl_interrupted;
compl_interrupted = FALSE;
@@ -4945,15 +4951,10 @@ int get_literal(void)
return cc;
}
-/*
- * Insert character, taking care of special keys and mod_mask
- */
-static void
-insert_special (
- int c,
- int allow_modmask,
- int ctrlv /* c was typed after CTRL-V */
-)
+/// Insert character, taking care of special keys and mod_mask
+///
+/// @param ctrlv `c` was typed after CTRL-V
+static void insert_special(int c, int allow_modmask, int ctrlv)
{
char_u *p;
int len;
@@ -4965,6 +4966,9 @@ insert_special (
* Only use mod_mask for special keys, to avoid things like <S-Space>,
* unless 'allow_modmask' is TRUE.
*/
+ if (mod_mask & MOD_MASK_CMD) { // Command-key never produces a normal key.
+ allow_modmask = true;
+ }
if (IS_SPECIAL(c) || (mod_mask && allow_modmask)) {
p = get_special_key_name(c, mod_mask);
len = (int)STRLEN(p);
@@ -6317,18 +6321,22 @@ char_u *get_last_insert_save(void)
return s;
}
-/*
- * Check the word in front of the cursor for an abbreviation.
- * Called when the non-id character "c" has been entered.
- * When an abbreviation is recognized it is removed from the text and
- * the replacement string is inserted in typebuf.tb_buf[], followed by "c".
- */
-static int echeck_abbr(int c)
+/// Check the word in front of the cursor for an abbreviation.
+/// Called when the non-id character "c" has been entered.
+/// When an abbreviation is recognized it is removed from the text and
+/// the replacement string is inserted in typebuf.tb_buf[], followed by "c".
+///
+/// @param c character
+///
+/// @return true if the word is a known abbreviation.
+static bool echeck_abbr(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- /* Don't check for abbreviation in paste mode, when disabled and just
- * after moving around with cursor keys. */
- if (p_paste || no_abbr || arrow_used)
- return FALSE;
+ // Don't check for abbreviation in paste mode, when disabled and just
+ // after moving around with cursor keys.
+ if (p_paste || no_abbr || arrow_used) {
+ return false;
+ }
return check_abbr(c, get_cursor_line_ptr(), curwin->w_cursor.col,
curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0);
@@ -6564,13 +6572,11 @@ static void replace_do_bs(int limit_col)
(void)del_char_after_col(limit_col);
}
-/*
- * Return TRUE if C-indenting is on.
- */
-static int cindent_on(void) {
- return !p_paste && (curbuf->b_p_cin
- || *curbuf->b_p_inde != NUL
- );
+/// Check that C-indenting is on.
+static bool cindent_on(void)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL);
}
/*
@@ -6600,32 +6606,33 @@ void fix_indent(void) {
do_c_expr_indent();
}
-/*
- * return TRUE if 'cinkeys' contains the key "keytyped",
- * when == '*': Only if key is preceded with '*' (indent before insert)
- * when == '!': Only if key is preceded with '!' (don't insert)
- * when == ' ': Only if key is not preceded with '*'(indent afterwards)
- *
- * "keytyped" can have a few special values:
- * KEY_OPEN_FORW
- * KEY_OPEN_BACK
- * KEY_COMPLETE just finished completion.
- *
- * If line_is_empty is TRUE accept keys with '0' before them.
- */
-int in_cinkeys(int keytyped, int when, int line_is_empty)
+/// Check that "cinkeys" contains the key "keytyped",
+/// when == '*': Only if key is preceded with '*' (indent before insert)
+/// when == '!': Only if key is preceded with '!' (don't insert)
+/// when == ' ': Only if key is not preceded with '*' (indent afterwards)
+///
+/// "keytyped" can have a few special values:
+/// KEY_OPEN_FORW :
+/// KEY_OPEN_BACK :
+/// KEY_COMPLETE : Just finished completion.
+///
+/// @param keytyped key that was typed
+/// @param when condition on when to perform the check
+/// @param line_is_empty when true, accept keys with '0' before them.
+bool in_cinkeys(int keytyped, int when, bool line_is_empty)
{
- char_u *look;
+ char_u *look;
int try_match;
int try_match_word;
- char_u *p;
- char_u *line;
+ char_u *p;
+ char_u *line;
int icase;
int i;
- if (keytyped == NUL)
- /* Can happen with CTRL-Y and CTRL-E on a short line. */
- return FALSE;
+ if (keytyped == NUL) {
+ // Can happen with CTRL-Y and CTRL-E on a short line.
+ return false;
+ }
if (*curbuf->b_p_inde != NUL)
look = curbuf->b_p_indk; /* 'indentexpr' set: use 'indentkeys' */
@@ -6641,68 +6648,64 @@ int in_cinkeys(int keytyped, int when, int line_is_empty)
case '!': try_match = (*look == '!'); break;
default: try_match = (*look != '*'); break;
}
- if (*look == '*' || *look == '!')
- ++look;
+ if (*look == '*' || *look == '!') {
+ look++;
+ }
- /*
- * If there is a '0', only accept a match if the line is empty.
- * But may still match when typing last char of a word.
- */
+ // If there is a '0', only accept a match if the line is empty.
+ // But may still match when typing last char of a word.
if (*look == '0') {
try_match_word = try_match;
- if (!line_is_empty)
- try_match = FALSE;
- ++look;
- } else
- try_match_word = FALSE;
+ if (!line_is_empty) {
+ try_match = false;
+ }
+ look++;
+ } else {
+ try_match_word = false;
+ }
- /*
- * does it look like a control character?
- */
- if (*look == '^'
- && look[1] >= '?' && look[1] <= '_'
- ) {
- if (try_match && keytyped == Ctrl_chr(look[1]))
- return TRUE;
+ // Does it look like a control character?
+ if (*look == '^' && look[1] >= '?' && look[1] <= '_') {
+ if (try_match && keytyped == Ctrl_chr(look[1])) {
+ return true;
+ }
look += 2;
- }
- /*
- * 'o' means "o" command, open forward.
- * 'O' means "O" command, open backward.
- */
- else if (*look == 'o') {
- if (try_match && keytyped == KEY_OPEN_FORW)
- return TRUE;
- ++look;
+
+ // 'o' means "o" command, open forward.
+ // 'O' means "O" command, open backward.
+ } else if (*look == 'o') {
+ if (try_match && keytyped == KEY_OPEN_FORW) {
+ return true;
+ }
+ look++;
} else if (*look == 'O') {
- if (try_match && keytyped == KEY_OPEN_BACK)
- return TRUE;
- ++look;
- }
- /*
- * 'e' means to check for "else" at start of line and just before the
- * cursor.
- */
- else if (*look == 'e') {
+ if (try_match && keytyped == KEY_OPEN_BACK) {
+ return true;
+ }
+ look++;
+
+ // 'e' means to check for "else" at start of line and just before the
+ // cursor.
+ } else if (*look == 'e') {
if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) {
p = get_cursor_line_ptr();
- if (skipwhite(p) == p + curwin->w_cursor.col - 4 &&
- STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0)
- return TRUE;
+ if (skipwhite(p) == p + curwin->w_cursor.col - 4
+ && STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) {
+ return true;
+ }
}
- ++look;
- }
- /*
- * ':' only causes an indent if it is at the end of a label or case
- * statement, or when it was before typing the ':' (to fix
- * class::method for C++).
- */
- else if (*look == ':') {
+ look++;
+
+ // ':' only causes an indent if it is at the end of a label or case
+ // statement, or when it was before typing the ':' (to fix
+ // class::method for C++).
+ } else if (*look == ':') {
if (try_match && keytyped == ':') {
p = get_cursor_line_ptr();
- if (cin_iscase(p, FALSE) || cin_isscopedecl(p) || cin_islabel())
- return TRUE;
- /* Need to get the line again after cin_islabel(). */
+ if (cin_iscase(p, false) || cin_isscopedecl(p) || cin_islabel()) {
+ return true;
+ }
+ // Need to get the line again after cin_islabel().
p = get_cursor_line_ptr();
if (curwin->w_cursor.col > 2
&& p[curwin->w_cursor.col - 1] == ':'
@@ -6712,28 +6715,27 @@ int in_cinkeys(int keytyped, int when, int line_is_empty)
|| cin_islabel());
p = get_cursor_line_ptr();
p[curwin->w_cursor.col - 1] = ':';
- if (i)
- return TRUE;
+ if (i) {
+ return true;
+ }
}
}
- ++look;
- }
- /*
- * Is it a key in <>, maybe?
- */
- else if (*look == '<') {
+ look++;
+
+ // Is it a key in <>, maybe?
+ } else if (*look == '<') {
if (try_match) {
- /*
- * make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>,
- * <:> and <!> so that people can re-indent on o, O, e, 0, <,
- * >, *, : and ! keys if they really really want to.
- */
+ // make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>,
+ // <:> and <!> so that people can re-indent on o, O, e, 0, <,
+ // >, *, : and ! keys if they really really want to.
if (vim_strchr((char_u *)"<>!*oOe0:", look[1]) != NULL
- && keytyped == look[1])
- return TRUE;
+ && keytyped == look[1]) {
+ return true;
+ }
- if (keytyped == get_special_key_code(look + 1))
- return TRUE;
+ if (keytyped == get_special_key_code(look + 1)) {
+ return true;
+ }
}
while (*look && *look != '>')
look++;
@@ -6804,18 +6806,18 @@ int in_cinkeys(int keytyped, int when, int line_is_empty)
(int)(curwin->w_cursor.col - (p - look)))
match = FALSE;
}
- if (match)
- return TRUE;
+ if (match) {
+ return true;
+ }
}
look = p;
- }
- /*
- * ok, it's a boring generic character.
- */
- else {
- if (try_match && *look == keytyped)
- return TRUE;
- ++look;
+
+ // Ok, it's a boring generic character.
+ } else {
+ if (try_match && *look == keytyped) {
+ return true;
+ }
+ look++;
}
/*
@@ -6823,7 +6825,7 @@ int in_cinkeys(int keytyped, int when, int line_is_empty)
*/
look = skip_to_option_part(look);
}
- return FALSE;
+ return false;
}
/*
@@ -7050,27 +7052,24 @@ static void ins_ctrl_hat(void)
status_redraw_curbuf();
}
-/*
- * Handle ESC in insert mode.
- * Returns TRUE when leaving insert mode, FALSE when going to repeat the
- * insert.
- */
-static int
-ins_esc (
- long *count,
- int cmdchar,
- int nomove /* don't move cursor */
-)
+/// Handle ESC in insert mode.
+///
+/// @param[in,out] count repeat count of the insert command
+/// @param cmdchar command that started the insert
+/// @param nomove when true, don't move the cursor
+///
+/// @return true when leaving insert mode, false when repeating the insert.
+static bool ins_esc(long *count, int cmdchar, bool nomove)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- int temp;
- static int disabled_redraw = FALSE;
+ static bool disabled_redraw = false;
check_spell_redraw();
- temp = curwin->w_cursor.col;
+ int temp = curwin->w_cursor.col;
if (disabled_redraw) {
- --RedrawingDisabled;
- disabled_redraw = FALSE;
+ RedrawingDisabled--;
+ disabled_redraw = false;
}
if (!arrow_used) {
/*
@@ -7100,9 +7099,10 @@ ins_esc (
if (cmdchar == 'r' || cmdchar == 'v') {
stuffRedoReadbuff(ESC_STR); // No ESC in redo buffer
}
- ++RedrawingDisabled;
- disabled_redraw = TRUE;
- return FALSE; /* repeat the insert */
+ RedrawingDisabled++;
+ disabled_redraw = true;
+ // Repeat the insert
+ return false;
}
stop_insert(&curwin->w_cursor, TRUE, nomove);
undisplay_dollar();
@@ -7152,16 +7152,15 @@ ins_esc (
setmouse();
ui_cursor_shape(); /* may show different cursor shape */
- /*
- * When recording or for CTRL-O, need to display the new mode.
- * Otherwise remove the mode message.
- */
- if (Recording || restart_edit != NUL)
+ // When recording or for CTRL-O, need to display the new mode.
+ // Otherwise remove the mode message.
+ if (Recording || restart_edit != NUL) {
showmode();
- else if (p_smd)
+ } else if (p_smd) {
MSG("");
-
- return TRUE; /* exit Insert mode */
+ }
+ // Exit Insert mode
+ return true;
}
/*
@@ -7199,14 +7198,16 @@ static void ins_ctrl_(void)
showmode();
}
-/*
- * If 'keymodel' contains "startsel", may start selection.
- * Returns TRUE when a CTRL-O and other keys stuffed.
- */
-static int ins_start_select(int c)
+/// If 'keymodel' contains "startsel", may start selection.
+///
+/// @param c character to check
+//
+/// @return true when a CTRL-O and other keys stuffed.
+static bool ins_start_select(int c)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
if (!km_startsel) {
- return FALSE;
+ return false;
}
switch (c) {
case K_KHOME:
@@ -7217,32 +7218,27 @@ static int ins_start_select(int c)
case K_KPAGEDOWN:
if (!(mod_mask & MOD_MASK_SHIFT))
break;
- /* FALLTHROUGH */
+ // FALLTHROUGH
case K_S_LEFT:
case K_S_RIGHT:
case K_S_UP:
case K_S_DOWN:
case K_S_END:
case K_S_HOME:
- /* Start selection right away, the cursor can move with
- * CTRL-O when beyond the end of the line. */
+ // Start selection right away, the cursor can move with
+ // CTRL-O when beyond the end of the line.
start_selection();
- /* Execute the key in (insert) Select mode. */
+ // Execute the key in (insert) Select mode.
stuffcharReadbuff(Ctrl_O);
if (mod_mask) {
- char_u buf[4];
-
- buf[0] = K_SPECIAL;
- buf[1] = KS_MODIFIER;
- buf[2] = mod_mask;
- buf[3] = NUL;
+ char_u buf[4] = { K_SPECIAL, KS_MODIFIER, mod_mask, NUL };
stuffReadbuff(buf);
}
stuffcharReadbuff(c);
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/*
@@ -7256,15 +7252,15 @@ static void ins_insert(int replaceState)
return;
}
- set_vim_var_string(VV_INSERTMODE,
- (char_u *)((State & REPLACE_FLAG) ? "i" :
- replaceState == VREPLACE ? "v" :
- "r"), 1);
- apply_autocmds(EVENT_INSERTCHANGE, NULL, NULL, FALSE, curbuf);
- if (State & REPLACE_FLAG)
+ set_vim_var_string(VV_INSERTMODE, ((State & REPLACE_FLAG) ? "i" :
+ replaceState == VREPLACE ? "v" :
+ "r"), 1);
+ apply_autocmds(EVENT_INSERTCHANGE, NULL, NULL, false, curbuf);
+ if (State & REPLACE_FLAG) {
State = INSERT | (State & LANGMAP);
- else
+ } else {
State = replaceState | (State & LANGMAP);
+ }
AppendCharToRedobuff(K_INS);
showmode();
ui_cursor_shape(); /* may show different cursor shape */
@@ -7366,18 +7362,23 @@ static void ins_bs_one(colnr_T *vcolp)
(void)del_char(FALSE);
}
-/*
- * Handle Backspace, delete-word and delete-line in Insert mode.
- * Return TRUE when backspace was actually used.
- */
-static int ins_bs(int c, int mode, int *inserted_space_p)
+/// Handle Backspace, delete-word and delete-line in Insert mode.
+///
+/// @param c charcter that was typed
+/// @param mode backspace mode to use
+/// @param[in,out] inserted_space_p whether a space was the last
+// character inserted
+///
+/// @return true when backspace was actually used.
+static bool ins_bs(int c, int mode, int *inserted_space_p)
+ FUNC_ATTR_NONNULL_ARG(3)
{
linenr_T lnum;
int cc;
int temp = 0; /* init for GCC */
colnr_T save_col;
colnr_T mincol;
- int did_backspace = FALSE;
+ bool did_backspace = false;
int in_indent;
int oldState;
int cpc[MAX_MCO]; /* composing characters */
@@ -7388,43 +7389,43 @@ static int ins_bs(int c, int mode, int *inserted_space_p)
* can't backup past starting point unless 'backspace' > 1
* can backup to a previous line if 'backspace' == 0
*/
- if ( bufempty()
- || (
- !revins_on &&
- ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0)
- || (!can_bs(BS_START)
- && (arrow_used
- || (curwin->w_cursor.lnum == Insstart_orig.lnum
- && curwin->w_cursor.col <= Insstart_orig.col)))
- || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0
- && curwin->w_cursor.col <= ai_col)
- || (!can_bs(BS_EOL) && curwin->w_cursor.col == 0)))) {
+ if (bufempty()
+ || (!revins_on
+ && ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0)
+ || (!can_bs(BS_START)
+ && (arrow_used
+ || (curwin->w_cursor.lnum == Insstart_orig.lnum
+ && curwin->w_cursor.col <= Insstart_orig.col)))
+ || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0
+ && curwin->w_cursor.col <= ai_col)
+ || (!can_bs(BS_EOL) && curwin->w_cursor.col == 0)))) {
vim_beep(BO_BS);
return false;
}
- if (stop_arrow() == FAIL)
- return FALSE;
+ if (stop_arrow() == FAIL) {
+ return false;
+ }
in_indent = inindent(0);
- if (in_indent)
- can_cindent = FALSE;
- end_comment_pending = NUL; /* After BS, don't auto-end comment */
- if (revins_on) /* put cursor after last inserted char */
+ if (in_indent) {
+ can_cindent = false;
+ }
+ end_comment_pending = NUL; // After BS, don't auto-end comment
+ if (revins_on) { // put cursor after last inserted char
inc_cursor();
-
- /* Virtualedit:
- * BACKSPACE_CHAR eats a virtual space
- * BACKSPACE_WORD eats all coladd
- * BACKSPACE_LINE eats all coladd and keeps going
- */
+ }
+ // Virtualedit:
+ // BACKSPACE_CHAR eats a virtual space
+ // BACKSPACE_WORD eats all coladd
+ // BACKSPACE_LINE eats all coladd and keeps going
if (curwin->w_cursor.coladd > 0) {
if (mode == BACKSPACE_CHAR) {
- --curwin->w_cursor.coladd;
- return TRUE;
+ curwin->w_cursor.coladd--;
+ return true;
}
if (mode == BACKSPACE_WORD) {
curwin->w_cursor.coladd = 0;
- return TRUE;
+ return true;
}
curwin->w_cursor.coladd = 0;
}
@@ -7436,10 +7437,10 @@ static int ins_bs(int c, int mode, int *inserted_space_p)
lnum = Insstart.lnum;
if (curwin->w_cursor.lnum == lnum || revins_on) {
if (u_save((linenr_T)(curwin->w_cursor.lnum - 2),
- (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) {
- return FALSE;
+ (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) {
+ return false;
}
- --Insstart.lnum;
+ Insstart.lnum--;
Insstart.col = MAXCOL;
}
/*
@@ -7643,14 +7644,14 @@ static int ins_bs(int c, int mode, int *inserted_space_p)
if (revins_on && gchar_cursor() == NUL)
break;
}
- /* Just a single backspace?: */
- if (mode == BACKSPACE_CHAR)
+ // Just a single backspace?:
+ if (mode == BACKSPACE_CHAR) {
break;
- } while (
- revins_on ||
- (curwin->w_cursor.col > mincol
- && (curwin->w_cursor.lnum != Insstart_orig.lnum
- || curwin->w_cursor.col != Insstart_orig.col)));
+ }
+ } while (revins_on
+ || (curwin->w_cursor.col > mincol
+ && (curwin->w_cursor.lnum != Insstart_orig.lnum
+ || curwin->w_cursor.col != Insstart_orig.col)));
}
did_backspace = true;
}
@@ -7681,12 +7682,12 @@ static int ins_bs(int c, int mode, int *inserted_space_p)
if (vim_strchr(p_cpo, CPO_BACKSPACE) != NULL && dollar_vcol == -1)
dollar_vcol = curwin->w_virtcol;
- /* When deleting a char the cursor line must never be in a closed fold.
- * E.g., when 'foldmethod' is indent and deleting the first non-white
- * char before a Tab. */
- if (did_backspace)
+ // When deleting a char the cursor line must never be in a closed fold.
+ // E.g., when 'foldmethod' is indent and deleting the first non-white
+ // char before a Tab.
+ if (did_backspace) {
foldOpenCursor();
-
+ }
return did_backspace;
}
@@ -7752,6 +7753,8 @@ static void ins_mousescroll(int dir)
(long)(curwin->w_botline - curwin->w_topline));
else
scroll_redraw(dir, 3L);
+ } else {
+ mouse_scroll_horiz(dir);
}
did_scroll = TRUE;
}
@@ -8005,35 +8008,37 @@ static void ins_pagedown(void)
}
}
-/*
- * Handle TAB in Insert or Replace mode.
- * Return TRUE when the TAB needs to be inserted like a normal character.
- */
-static int ins_tab(void)
+/// Handle TAB in Insert or Replace mode.
+///
+/// @return true when the TAB needs to be inserted like a normal character.
+static bool ins_tab(void)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
- int ind;
int i;
int temp;
- if (Insstart_blank_vcol == MAXCOL && curwin->w_cursor.lnum == Insstart.lnum)
+ if (Insstart_blank_vcol == MAXCOL && curwin->w_cursor.lnum == Insstart.lnum) {
Insstart_blank_vcol = get_nolist_virtcol();
- if (echeck_abbr(TAB + ABBR_OFF))
- return FALSE;
+ }
+ if (echeck_abbr(TAB + ABBR_OFF)) {
+ return false;
+ }
- ind = inindent(0);
- if (ind)
- can_cindent = FALSE;
+ int ind = inindent(0);
+ if (ind) {
+ can_cindent = false;
+ }
- /*
- * When nothing special, insert TAB like a normal character
- */
+ // When nothing special, insert TAB like a normal character
if (!curbuf->b_p_et
&& !(p_sta && ind && curbuf->b_p_ts != get_sw_value(curbuf))
- && get_sts_value() == 0)
- return TRUE;
+ && get_sts_value() == 0) {
+ return true;
+ }
- if (stop_arrow() == FAIL)
- return TRUE;
+ if (stop_arrow() == FAIL) {
+ return true;
+ }
did_ai = FALSE;
did_si = FALSE;
@@ -8041,12 +8046,13 @@ static int ins_tab(void)
can_si_back = FALSE;
AppendToRedobuff((char_u *)"\t");
- if (p_sta && ind) /* insert tab in indent, use 'shiftwidth' */
+ if (p_sta && ind) { // insert tab in indent, use "shiftwidth"
temp = get_sw_value(curbuf);
- else if (curbuf->b_p_sts != 0) /* use 'softtabstop' when set */
+ } else if (curbuf->b_p_sts != 0) { // use "softtabstop" when set
temp = get_sts_value();
- else /* otherwise use 'tabstop' */
+ } else { // otherwise use "tabstop"
temp = (int)curbuf->b_p_ts;
+ }
temp -= get_nolist_virtcol() % temp;
/*
@@ -8186,21 +8192,20 @@ static int ins_tab(void)
curwin->w_p_list = save_list;
}
- return FALSE;
+ return false;
}
-/*
- * Handle CR or NL in insert mode.
- * Return TRUE when it can't undo.
- */
-static int ins_eol(int c)
+/// Handle CR or NL in insert mode.
+///
+/// @return true when it can't undo.
+static bool ins_eol(int c)
{
- int i;
-
- if (echeck_abbr(c + ABBR_OFF))
- return FALSE;
- if (stop_arrow() == FAIL)
- return TRUE;
+ if (echeck_abbr(c + ABBR_OFF)) {
+ return false;
+ }
+ if (stop_arrow() == FAIL) {
+ return true;
+ }
undisplay_dollar();
/*
@@ -8233,9 +8238,9 @@ static int ins_eol(int c)
curwin->w_cursor.col += (colnr_T)STRLEN(get_cursor_pos_ptr());
AppendToRedobuff(NL_STR);
- i = open_line(FORWARD,
- has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM :
- 0, old_indent);
+ bool i = open_line(FORWARD,
+ has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : 0,
+ old_indent);
old_indent = 0;
can_cindent = TRUE;
/* When inserting a line the cursor line must never be in a closed fold. */
@@ -8489,22 +8494,22 @@ static colnr_T get_nolist_virtcol(void)
*/
static char_u *do_insert_char_pre(int c)
{
- char_u buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXBYTES + 1];
// Return quickly when there is nothing to do.
if (!has_event(EVENT_INSERTCHARPRE)) {
return NULL;
}
if (has_mbyte) {
- buf[(*mb_char2bytes)(c, buf)] = NUL;
+ buf[(*mb_char2bytes)(c, (char_u *) buf)] = NUL;
} else {
buf[0] = c;
buf[1] = NUL;
}
- /* Lock the text to avoid weird things from happening. */
- ++textlock;
- set_vim_var_string(VV_CHAR, buf, -1); /* set v:char */
+ // Lock the text to avoid weird things from happening.
+ textlock++;
+ set_vim_var_string(VV_CHAR, buf, -1);
char_u *res = NULL;
if (apply_autocmds(EVENT_INSERTCHARPRE, NULL, NULL, FALSE, curbuf)) {
@@ -8515,8 +8520,25 @@ static char_u *do_insert_char_pre(int c)
res = vim_strsave(get_vim_var_str(VV_CHAR));
}
- set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */
- --textlock;
+ set_vim_var_string(VV_CHAR, NULL, -1);
+ textlock--;
return res;
}
+
+static void show_pum(int save_w_wrow)
+{
+ // RedrawingDisabled may be set when invoked through complete().
+ int n = RedrawingDisabled;
+ RedrawingDisabled = 0;
+
+ // If the cursor moved we need to remove the pum first.
+ setcursor();
+ if (save_w_wrow != curwin->w_wrow) {
+ ins_compl_del_pum();
+ }
+
+ ins_compl_show_pum();
+ setcursor();
+ RedrawingDisabled = n;
+}
diff --git a/src/nvim/edit.h b/src/nvim/edit.h
index 0289b2c3a6..0d61f26bcc 100644
--- a/src/nvim/edit.h
+++ b/src/nvim/edit.h
@@ -36,12 +36,6 @@ typedef int (*IndentGetter)(void);
#define INSCHAR_NO_FEX 8 /* don't use 'formatexpr' */
#define INSCHAR_COM_LIST 16 /* format comments with list/2nd line indent */
-/* direction for nv_mousescroll() and ins_mousescroll() */
-#define MSCR_DOWN 0 /* DOWN must be FALSE */
-#define MSCR_UP 1
-#define MSCR_LEFT -1
-#define MSCR_RIGHT -2
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "edit.h.generated.h"
#endif
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a9af7d94c1..420a712e3e 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -66,18 +66,20 @@
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
-#include "nvim/tempfile.h"
#include "nvim/ui.h"
#include "nvim/mouse.h"
#include "nvim/terminal.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/window.h"
+#include "nvim/eval/encode.h"
+#include "nvim/eval/decode.h"
#include "nvim/os/os.h"
#include "nvim/event/libuv_process.h"
#include "nvim/event/pty_process.h"
#include "nvim/event/rstream.h"
#include "nvim/event/wstream.h"
+#include "nvim/event/time.h"
#include "nvim/os/time.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
@@ -87,7 +89,6 @@
#include "nvim/os/dl.h"
#include "nvim/os/input.h"
#include "nvim/event/loop.h"
-#include "nvim/lib/kvec.h"
#include "nvim/lib/queue.h"
#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */
@@ -142,13 +143,6 @@ typedef struct lval_S {
char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */
} lval_T;
-/// Structure defining state for read_from_list()
-typedef struct {
- const listitem_T *li; ///< Item currently read.
- size_t offset; ///< Byte offset inside the read item.
- size_t li_length; ///< Length of the string inside the read item.
-} ListReaderState;
-
static char *e_letunexp = N_("E18: Unexpected characters in :let");
static char *e_listidx = N_("E684: list index out of range: %" PRId64);
@@ -160,6 +154,7 @@ static char *e_listdictarg = N_(
static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary");
static char *e_listreq = N_("E714: List required");
static char *e_dictreq = N_("E715: Dictionary required");
+static char *e_strreq = N_("E114: String required");
static char *e_toomanyarg = N_("E118: Too many arguments for function: %s");
static char *e_dictkey = N_("E716: Key not present in Dictionary: %s");
static char *e_funcexts = N_(
@@ -173,6 +168,7 @@ static char *e_illvar = N_("E461: Illegal variable name: %s");
static char *e_float_as_string = N_("E806: using Float as a String");
static char_u * const empty_string = (char_u *)"";
+static char_u * const namespace_char = (char_u *)"abglstvw";
static dictitem_T globvars_var; /* variable used for g: */
#define globvarht globvardict.dv_hashtab
@@ -183,17 +179,7 @@ static dictitem_T globvars_var; /* variable used for g: */
*/
static hashtab_T compat_hashtab;
-/*
- * When recursively copying lists and dicts we need to remember which ones we
- * have done to avoid endless recursiveness. This unique ID is used for that.
- * The last bit is used for previous_funccal, ignored when comparing.
- */
-static int current_copyID = 0;
-#define COPYID_INC 2
-#define COPYID_MASK (~0x1)
-
-/// Abort conversion to string after a recursion error.
-static bool did_echo_string_emsg = false;
+hashtab_T func_hashtab;
/*
* Array to hold the hashtab with variables local to each sourced script.
@@ -296,93 +282,112 @@ typedef enum {
#define VV_RO 2 /* read-only */
#define VV_RO_SBX 4 /* read-only in the sandbox */
-#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}}, {0}
+#define VV(idx, name, type, flags) \
+ [idx] = { \
+ .vv_name = name, \
+ .vv_di = { \
+ .di_tv = { .v_type = type }, \
+ .di_flags = 0, \
+ }, \
+ .vv_filler = { 0 }, \
+ .vv_flags = flags, \
+ }
// Array to hold the value of v: variables.
// The value is in a dictitem, so that it can also be used in the v: scope.
// The reason to use this table anyway is for very quick access to the
// variables with the VV_ defines.
static struct vimvar {
- char *vv_name; /* name of variable, without v: */
- dictitem_T vv_di; /* value and name for key */
- char vv_filler[16]; /* space for LONGEST name below!!! */
- char vv_flags; /* VV_COMPAT, VV_RO, VV_RO_SBX */
-} vimvars[VV_LEN] =
-{
- /*
- * The order here must match the VV_ defines in eval.h!
- * Initializing a union does not work, leave tv.vval empty to get zero's.
- */
- { VV_NAME("count", VAR_NUMBER), VV_COMPAT+VV_RO },
- { VV_NAME("count1", VAR_NUMBER), VV_RO },
- { VV_NAME("prevcount", VAR_NUMBER), VV_RO },
- { VV_NAME("errmsg", VAR_STRING), VV_COMPAT },
- { VV_NAME("warningmsg", VAR_STRING), 0 },
- { VV_NAME("statusmsg", VAR_STRING), 0 },
- { VV_NAME("shell_error", VAR_NUMBER), VV_COMPAT+VV_RO },
- { VV_NAME("this_session", VAR_STRING), VV_COMPAT },
- { VV_NAME("version", VAR_NUMBER), VV_COMPAT+VV_RO },
- { VV_NAME("lnum", VAR_NUMBER), VV_RO_SBX },
- { VV_NAME("termresponse", VAR_STRING), VV_RO },
- { VV_NAME("fname", VAR_STRING), VV_RO },
- { VV_NAME("lang", VAR_STRING), VV_RO },
- { VV_NAME("lc_time", VAR_STRING), VV_RO },
- { VV_NAME("ctype", VAR_STRING), VV_RO },
- { VV_NAME("charconvert_from", VAR_STRING), VV_RO },
- { VV_NAME("charconvert_to", VAR_STRING), VV_RO },
- { VV_NAME("fname_in", VAR_STRING), VV_RO },
- { VV_NAME("fname_out", VAR_STRING), VV_RO },
- { VV_NAME("fname_new", VAR_STRING), VV_RO },
- { VV_NAME("fname_diff", VAR_STRING), VV_RO },
- { VV_NAME("cmdarg", VAR_STRING), VV_RO },
- { VV_NAME("foldstart", VAR_NUMBER), VV_RO_SBX },
- { VV_NAME("foldend", VAR_NUMBER), VV_RO_SBX },
- { VV_NAME("folddashes", VAR_STRING), VV_RO_SBX },
- { VV_NAME("foldlevel", VAR_NUMBER), VV_RO_SBX },
- { VV_NAME("progname", VAR_STRING), VV_RO },
- { VV_NAME("servername", VAR_STRING), VV_RO },
- { VV_NAME("dying", VAR_NUMBER), VV_RO },
- { VV_NAME("exception", VAR_STRING), VV_RO },
- { VV_NAME("throwpoint", VAR_STRING), VV_RO },
- { VV_NAME("register", VAR_STRING), VV_RO },
- { VV_NAME("cmdbang", VAR_NUMBER), VV_RO },
- { VV_NAME("insertmode", VAR_STRING), VV_RO },
- { VV_NAME("val", VAR_UNKNOWN), VV_RO },
- { VV_NAME("key", VAR_UNKNOWN), VV_RO },
- { VV_NAME("profiling", VAR_NUMBER), VV_RO },
- { VV_NAME("fcs_reason", VAR_STRING), VV_RO },
- { VV_NAME("fcs_choice", VAR_STRING), 0 },
- { VV_NAME("beval_bufnr", VAR_NUMBER), VV_RO },
- { VV_NAME("beval_winnr", VAR_NUMBER), VV_RO },
- { VV_NAME("beval_lnum", VAR_NUMBER), VV_RO },
- { VV_NAME("beval_col", VAR_NUMBER), VV_RO },
- { VV_NAME("beval_text", VAR_STRING), VV_RO },
- { VV_NAME("scrollstart", VAR_STRING), 0 },
- { VV_NAME("swapname", VAR_STRING), VV_RO },
- { VV_NAME("swapchoice", VAR_STRING), 0 },
- { VV_NAME("swapcommand", VAR_STRING), VV_RO },
- { VV_NAME("char", VAR_STRING), 0 },
- { VV_NAME("mouse_win", VAR_NUMBER), 0 },
- { VV_NAME("mouse_lnum", VAR_NUMBER), 0 },
- { VV_NAME("mouse_col", VAR_NUMBER), 0 },
- { VV_NAME("operator", VAR_STRING), VV_RO },
- { VV_NAME("searchforward", VAR_NUMBER), 0 },
- { VV_NAME("hlsearch", VAR_NUMBER), 0 },
- { VV_NAME("oldfiles", VAR_LIST), 0 },
- { VV_NAME("windowid", VAR_NUMBER), VV_RO },
- { VV_NAME("progpath", VAR_STRING), VV_RO },
- { VV_NAME("command_output", VAR_STRING), 0 },
- { VV_NAME("completed_item", VAR_DICT), VV_RO },
- { VV_NAME("option_new", VAR_STRING), VV_RO },
- { VV_NAME("option_old", VAR_STRING), VV_RO },
- { VV_NAME("option_type", VAR_STRING), VV_RO },
- { VV_NAME("errors", VAR_LIST), 0 },
- { VV_NAME("msgpack_types", VAR_DICT), VV_RO },
+ char *vv_name; ///< Name of the variable, without v:.
+ dictitem_T vv_di; ///< Value of the variable, with name.
+ char vv_filler[16]; ///< Space for longest name from below.
+ char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX.
+} vimvars[] =
+{
+ // VV_ tails differing from upcased string literals:
+ // VV_CC_FROM "charconvert_from"
+ // VV_CC_TO "charconvert_to"
+ // VV_SEND_SERVER "servername"
+ // VV_REG "register"
+ // VV_OP "operator"
+ VV(VV_COUNT, "count", VAR_NUMBER, VV_COMPAT+VV_RO),
+ VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO),
+ VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO),
+ VV(VV_ERRMSG, "errmsg", VAR_STRING, VV_COMPAT),
+ VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0),
+ VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0),
+ VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_COMPAT+VV_RO),
+ VV(VV_THIS_SESSION, "this_session", VAR_STRING, VV_COMPAT),
+ VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO),
+ VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO),
+ VV(VV_FNAME, "fname", VAR_STRING, VV_RO),
+ VV(VV_LANG, "lang", VAR_STRING, VV_RO),
+ VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO),
+ VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO),
+ VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO),
+ VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO),
+ VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO),
+ VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO),
+ VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO),
+ VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO),
+ VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO),
+ VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX),
+ VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO),
+ VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO),
+ VV(VV_DYING, "dying", VAR_NUMBER, VV_RO),
+ VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO),
+ VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO),
+ VV(VV_REG, "register", VAR_STRING, VV_RO),
+ VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO),
+ VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO),
+ VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO),
+ VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO),
+ VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO),
+ VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO),
+ VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0),
+ VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO),
+ VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0),
+ VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO),
+ VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0),
+ VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO),
+ VV(VV_CHAR, "char", VAR_STRING, 0),
+ VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0),
+ VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0),
+ VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0),
+ VV(VV_OP, "operator", VAR_STRING, VV_RO),
+ VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0),
+ VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0),
+ VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0),
+ VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO),
+ VV(VV_COMMAND_OUTPUT, "command_output", VAR_STRING, 0),
+ VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO),
+ VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO),
+ VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO),
+ VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO),
+ VV(VV_ERRORS, "errors", VAR_LIST, 0),
+ VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
+ VV(VV_EVENT, "event", VAR_DICT, VV_RO),
+ VV(VV_FALSE, "false", VAR_SPECIAL, VV_RO),
+ VV(VV_TRUE, "true", VAR_SPECIAL, VV_RO),
+ VV(VV_NULL, "null", VAR_SPECIAL, VV_RO),
+ VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
+ VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
};
+#undef VV
/* shorthand */
#define vv_type vv_di.di_tv.v_type
#define vv_nr vv_di.di_tv.vval.v_number
+#define vv_special vv_di.di_tv.vval.v_special
#define vv_float vv_di.di_tv.vval.v_float
#define vv_str vv_di.di_tv.vval.v_string
#define vv_list vv_di.di_tv.vval.v_list
@@ -416,29 +421,6 @@ typedef struct dict_watcher {
bool busy; // prevent recursion if the dict is changed in the callback
} DictWatcher;
-/// Structure representing current VimL to messagepack conversion state
-typedef struct {
- enum {
- kMPConvDict, ///< Convert dict_T *dictionary.
- kMPConvList, ///< Convert list_T *list.
- kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
- } type;
- union {
- struct {
- dict_T *dict; ///< Currently converted dictionary.
- hashitem_T *hi; ///< Currently converted dictionary item.
- size_t todo; ///< Amount of items left to process.
- } d; ///< State of dictionary conversion.
- struct {
- list_T *list; ///< Currently converted list.
- listitem_T *li; ///< Currently converted list item.
- } l; ///< State of list or generic mapping conversion.
- } data; ///< Data to convert.
-} MPConvStackVal;
-
-/// Stack used to convert VimL values to messagepack.
-typedef kvec_t(MPConvStackVal) MPConvStack;
-
typedef struct {
TerminalJobData *data;
ufunc_T *callback;
@@ -447,6 +429,14 @@ typedef struct {
int status;
} JobEvent;
+typedef struct {
+ TimeWatcher tw;
+ int timer_id;
+ int repeat_count;
+ bool stopped;
+ ufunc_T *callback;
+} timer_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.c.generated.h"
#endif
@@ -457,17 +447,9 @@ typedef struct {
static uint64_t current_job_id = 1;
static PMap(uint64_t) *jobs = NULL;
-typedef enum {
- kMPNil,
- kMPBoolean,
- kMPInteger,
- kMPFloat,
- kMPString,
- kMPBinary,
- kMPArray,
- kMPMap,
- kMPExt,
-} MessagePackType;
+static uint64_t last_timer_id = 0;
+static PMap(uint64_t) *timers = NULL;
+
static const char *const msgpack_type_names[] = {
[kMPNil] = "nil",
[kMPBoolean] = "boolean",
@@ -479,7 +461,7 @@ static const char *const msgpack_type_names[] = {
[kMPMap] = "map",
[kMPExt] = "ext",
};
-static const list_T *msgpack_type_lists[] = {
+const list_T *eval_msgpack_type_lists[] = {
[kMPNil] = NULL,
[kMPBoolean] = NULL,
[kMPInteger] = NULL,
@@ -496,8 +478,10 @@ static const list_T *msgpack_type_lists[] = {
*/
void eval_init(void)
{
+ vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
+
jobs = pmap_new(uint64_t)();
- int i;
+ timers = pmap_new(uint64_t)();
struct vimvar *p;
init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
@@ -506,7 +490,7 @@ void eval_init(void)
hash_init(&compat_hashtab);
hash_init(&func_hashtab);
- for (i = 0; i < VV_LEN; ++i) {
+ for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
p = &vimvars[i];
STRCPY(p->vv_di.di_key, p->vv_name);
if (p->vv_flags & VV_RO)
@@ -535,7 +519,7 @@ void eval_init(void)
.v_type = VAR_LIST,
.vval = { .v_list = type_list, },
};
- msgpack_type_lists[i] = type_list;
+ eval_msgpack_type_lists[i] = type_list;
if (dict_add(msgpack_types_dict, di) == FAIL) {
// There must not be duplicate items in this dictionary by definition.
assert(false);
@@ -545,10 +529,19 @@ void eval_init(void)
set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
+
+ dict_T *v_event = dict_alloc();
+ v_event->dv_lock = VAR_FIXED;
+ set_vim_var_dict(VV_EVENT, v_event);
set_vim_var_list(VV_ERRORS, list_alloc());
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
- set_reg_var(0); /* default for v:register is not 0 but '"' */
+
+ set_vim_var_special(VV_FALSE, kSpecialVarFalse);
+ set_vim_var_special(VV_TRUE, kSpecialVarTrue);
+ set_vim_var_special(VV_NULL, kSpecialVarNull);
+
+ set_reg_var(0); // default for v:register is not 0 but '"'
}
#if defined(EXITFREE)
@@ -556,7 +549,7 @@ void eval_clear(void)
{
struct vimvar *p;
- for (int i = 0; i < VV_LEN; ++i) {
+ for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
p = &vimvars[i];
if (p->vv_di.di_tv.v_type == VAR_STRING) {
xfree(p->vv_str);
@@ -791,45 +784,50 @@ void var_redir_stop(void)
redir_varname = NULL;
}
-int eval_charconvert(char_u *enc_from, char_u *enc_to, char_u *fname_from, char_u *fname_to)
+int eval_charconvert(const char *const enc_from, const char *const enc_to,
+ const char *const fname_from, const char *const fname_to)
{
- int err = FALSE;
+ int err = false;
set_vim_var_string(VV_CC_FROM, enc_from, -1);
set_vim_var_string(VV_CC_TO, enc_to, -1);
set_vim_var_string(VV_FNAME_IN, fname_from, -1);
set_vim_var_string(VV_FNAME_OUT, fname_to, -1);
- if (eval_to_bool(p_ccv, &err, NULL, FALSE))
- err = TRUE;
+ if (eval_to_bool(p_ccv, &err, NULL, false)) {
+ err = true;
+ }
set_vim_var_string(VV_CC_FROM, NULL, -1);
set_vim_var_string(VV_CC_TO, NULL, -1);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
- if (err)
+ if (err) {
return FAIL;
+ }
return OK;
}
-int eval_printexpr(char_u *fname, char_u *args)
+int eval_printexpr(const char *const fname, const char *const args)
{
- int err = FALSE;
+ int err = false;
set_vim_var_string(VV_FNAME_IN, fname, -1);
set_vim_var_string(VV_CMDARG, args, -1);
- if (eval_to_bool(p_pexpr, &err, NULL, FALSE))
- err = TRUE;
+ if (eval_to_bool(p_pexpr, &err, NULL, false)) {
+ err = true;
+ }
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_CMDARG, NULL, -1);
if (err) {
- os_remove((char *)fname);
+ os_remove(fname);
return FAIL;
}
return OK;
}
-void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile)
+void eval_diff(const char *const origfile, const char *const newfile,
+ const char *const outfile)
{
int err = FALSE;
@@ -842,7 +840,8 @@ void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile)
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
}
-void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile)
+void eval_patch(const char *const origfile, const char *const difffile,
+ const char *const outfile)
{
int err;
@@ -1700,12 +1699,13 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first)
}
error = TRUE;
} else {
- if (tofree != NULL)
+ if (tofree != NULL) {
name = tofree;
- if (get_var_tv(name, len, &tv, TRUE, FALSE) == FAIL)
- error = TRUE;
- else {
- /* handle d.key, l[idx], f(expr) */
+ }
+ if (get_var_tv(name, len, &tv, NULL, true, false) == FAIL) {
+ error = true;
+ } else {
+ // handle d.key, l[idx], f(expr)
arg_subsc = arg;
if (handle_subscript(&arg, &tv, TRUE, TRUE) == FAIL)
error = TRUE;
@@ -1725,7 +1725,7 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first)
} else {
int c;
- char_u *s = (char_u *) echo_string(&tv, NULL);
+ char_u *s = (char_u *) encode_tv2echo(&tv, NULL);
c = *arg;
*arg = NUL;
list_one_var_a((char_u *)"",
@@ -2176,10 +2176,10 @@ get_lval (
if (len == -1)
clear_tv(&var1);
break;
- }
- /* existing variable, need to check if it can be changed */
- else if (var_check_ro(lp->ll_di->di_flags, name))
+ } else if (var_check_ro(lp->ll_di->di_flags, name, false)) {
+ // existing variable, need to check if it can be changed
return NULL;
+ }
if (len == -1)
clear_tv(&var1);
@@ -2274,11 +2274,16 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch
if (op != NULL && *op != '=') {
typval_T tv;
- /* handle +=, -= and .= */
+ // handle +=, -= and .=
+ di = NULL;
if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name),
- &tv, TRUE, FALSE) == OK) {
- if (tv_op(&tv, rettv, op) == OK)
- set_var(lp->ll_name, &tv, FALSE);
+ &tv, &di, true, false) == OK) {
+ if ((di == NULL
+ || (!var_check_ro(di->di_flags, lp->ll_name, false)
+ && !tv_check_lock(di->di_tv.v_lock, lp->ll_name, false)))
+ && tv_op(&tv, rettv, op) == OK) {
+ set_var(lp->ll_name, &tv, false);
+ }
clear_tv(&tv);
}
} else
@@ -2286,16 +2291,17 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch
*endp = cc;
}
} else if (tv_check_lock(lp->ll_newkey == NULL
- ? lp->ll_tv->v_lock
- : lp->ll_tv->vval.v_dict->dv_lock, lp->ll_name))
- ;
- else if (lp->ll_range) {
+ ? lp->ll_tv->v_lock
+ : lp->ll_tv->vval.v_dict->dv_lock,
+ lp->ll_name, false)) {
+ } else if (lp->ll_range) {
listitem_T *ll_li = lp->ll_li;
int ll_n1 = lp->ll_n1;
// Check whether any of the list items is locked
- for (listitem_T *ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; ) {
- if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name)) {
+ for (listitem_T *ri = rettv->vval.v_list->lv_first;
+ ri != NULL && ll_li != NULL; ) {
+ if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) {
return;
}
ri = ri->li_next;
@@ -2403,11 +2409,12 @@ static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
char_u numbuf[NUMBUFLEN];
char_u *s;
- /* Can't do anything with a Funcref or a Dict on the right. */
+ // Can't do anything with a Funcref, a Dict or special value on the right.
if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) {
switch (tv1->v_type) {
case VAR_DICT:
case VAR_FUNC:
+ case VAR_SPECIAL:
break;
case VAR_LIST:
@@ -2475,6 +2482,9 @@ static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
tv1->vval.v_float -= f;
}
return OK;
+
+ case VAR_UNKNOWN:
+ assert(false);
}
}
@@ -2891,9 +2901,9 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit)
ret = FAIL;
*name_end = cc;
} else if ((lp->ll_list != NULL
- && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name))
+ && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name, false))
|| (lp->ll_dict != NULL
- && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name))) {
+ && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name, false))) {
return FAIL;
} else if (lp->ll_range) {
listitem_T *li;
@@ -2902,7 +2912,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit)
while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) {
li = ll_li->li_next;
- if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name)) {
+ if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) {
return false;
}
ll_li = li;
@@ -2975,9 +2985,9 @@ int do_unlet(char_u *name, int forceit)
hi = hash_find(ht, varname);
if (!HASHITEM_EMPTY(hi)) {
di = HI2DI(hi);
- if (var_check_fixed(di->di_flags, name)
- || var_check_ro(di->di_flags, name)
- || tv_check_lock(d->dv_lock, name)) {
+ if (var_check_fixed(di->di_flags, name, false)
+ || var_check_ro(di->di_flags, name, false)
+ || tv_check_lock(d->dv_lock, name, false)) {
return FAIL;
}
typval_T oldtv;
@@ -3045,12 +3055,13 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock)
li = li->li_next;
++lp->ll_n1;
}
- } else if (lp->ll_list != NULL)
- /* (un)lock a List item. */
+ } else if (lp->ll_list != NULL) {
+ // (un)lock a List item.
item_lock(&lp->ll_li->li_tv, deep, lock);
- else
- /* un(lock) a Dictionary item. */
+ } else {
+ // (un)lock a Dictionary item.
item_lock(&lp->ll_di->di_tv, deep, lock);
+ }
return ret;
}
@@ -3111,6 +3122,15 @@ static void item_lock(typval_T *tv, int deep, int lock)
}
}
}
+ break;
+ case VAR_NUMBER:
+ case VAR_FLOAT:
+ case VAR_STRING:
+ case VAR_FUNC:
+ case VAR_SPECIAL:
+ break;
+ case VAR_UNKNOWN:
+ assert(false);
}
--recurse;
}
@@ -3189,7 +3209,7 @@ char_u *get_user_var_name(expand_T *xp, int idx)
static size_t bdone;
static size_t wdone;
static size_t tdone;
- static int vidx;
+ static size_t vidx;
static hashitem_T *hi;
hashtab_T *ht;
@@ -3251,9 +3271,10 @@ char_u *get_user_var_name(expand_T *xp, int idx)
return cat_prefix_varname('t', hi->hi_key);
}
- /* v: variables */
- if (vidx < VV_LEN)
+ // v: variables
+ if (vidx < ARRAY_SIZE(vimvars)) {
return cat_prefix_varname('v', (char_u *)vimvars[vidx++].vv_name);
+ }
xfree(varnamebuf);
varnamebuf = NULL;
@@ -3572,9 +3593,10 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
type = TYPE_SEQUAL;
break;
case 'i': if (p[1] == 's') {
- if (p[2] == 'n' && p[3] == 'o' && p[4] == 't')
+ if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') {
len = 5;
- if (!vim_isIDc(p[len])) {
+ }
+ if (!isalnum(p[len]) && p[len] != '_') {
type = len == 2 ? TYPE_EQUAL : TYPE_NEQUAL;
type_is = TRUE;
}
@@ -4001,12 +4023,22 @@ eval6 (
* When either side is a float the result is a float.
*/
if (use_float) {
- if (op == '*')
+ if (op == '*') {
f1 = f1 * f2;
- else if (op == '/') {
- /* We rely on the floating point library to handle divide
- * by zero to result in "inf" and not a crash. */
- f1 = f2 != 0 ? f1 / f2 : INFINITY;
+ } else if (op == '/') {
+ // Division by zero triggers error from AddressSanitizer
+ f1 = (f2 == 0
+ ? (
+#ifdef NAN
+ f1 == 0
+ ? NAN
+ :
+#endif
+ (f1 > 0
+ ? INFINITY
+ : -INFINITY)
+ )
+ : f1 / f2);
} else {
EMSG(_("E804: Cannot use '%' with Float"));
return FAIL;
@@ -4132,7 +4164,7 @@ static int eval7(
if (get_float) {
float_T f;
- *arg += string2float(*arg, &f);
+ *arg += string2float((char *) *arg, &f);
if (evaluate) {
rettv->v_type = VAR_FLOAT;
rettv->vval.v_float = f;
@@ -4239,7 +4271,7 @@ static int eval7(
ret = FAIL;
}
} else if (evaluate) {
- ret = get_var_tv(s, len, rettv, true, false);
+ ret = get_var_tv(s, len, rettv, NULL, true, false);
} else {
ret = OK;
}
@@ -4321,14 +4353,37 @@ eval_index (
char_u *s;
char_u *key = NULL;
- if (rettv->v_type == VAR_FUNC) {
- if (verbose)
- EMSG(_("E695: Cannot index a Funcref"));
- return FAIL;
- } else if (rettv->v_type == VAR_FLOAT) {
- if (verbose)
- EMSG(_(e_float_as_string));
- return FAIL;
+ switch (rettv->v_type) {
+ case VAR_FUNC: {
+ if (verbose) {
+ EMSG(_("E695: Cannot index a Funcref"));
+ }
+ return FAIL;
+ }
+ case VAR_FLOAT: {
+ if (verbose) {
+ EMSG(_(e_float_as_string));
+ }
+ return FAIL;
+ }
+ case VAR_SPECIAL: {
+ if (verbose) {
+ EMSG(_("E909: Cannot index a special variable"));
+ }
+ return FAIL;
+ }
+ case VAR_UNKNOWN: {
+ if (evaluate) {
+ return FAIL;
+ }
+ // fallthrough
+ }
+ case VAR_STRING:
+ case VAR_NUMBER:
+ case VAR_LIST:
+ case VAR_DICT: {
+ break;
+ }
}
init_tv(&var1);
@@ -4519,6 +4574,11 @@ eval_index (
*rettv = var1;
}
break;
+ case VAR_SPECIAL:
+ case VAR_FUNC:
+ case VAR_FLOAT:
+ case VAR_UNKNOWN:
+ break; // Not evaluating, skipping over subscript
}
}
@@ -4693,13 +4753,14 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
++name;
break;
- /* Special key, e.g.: "\<C-W>" */
- case '<': extra = trans_special(&p, name, TRUE);
+ // Special key, e.g.: "\<C-W>"
+ case '<':
+ extra = trans_special((const char_u **) &p, STRLEN(p), name, true);
if (extra != 0) {
name += extra;
break;
}
- /* FALLTHROUGH */
+ // FALLTHROUGH
default: MB_COPY_CHAR(p, name);
break;
@@ -5063,10 +5124,18 @@ tv_equal (
s1 = get_tv_string_buf(tv1, buf1);
s2 = get_tv_string_buf(tv2, buf2);
return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0;
+
+ case VAR_SPECIAL:
+ return tv1->vval.v_special == tv2->vval.v_special;
+
+ case VAR_UNKNOWN:
+ // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does
+ // not equal anything, not even self.
+ return false;
}
- EMSG2(_(e_intern2), "tv_equal()");
- return TRUE;
+ assert(false);
+ return false;
}
/*
@@ -5467,9 +5536,10 @@ static int list_join_inner(garray_T *const gap, list_T *const l,
for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) {
char *s;
size_t len;
- s = echo_string(&item->li_tv, &len);
- if (s == NULL)
+ s = encode_tv2echo(&item->li_tv, &len);
+ if (s == NULL) {
return FAIL;
+ }
sumlen += (int) len;
@@ -5477,9 +5547,6 @@ static int list_join_inner(garray_T *const gap, list_T *const l,
p->tofree = p->s = (char_u *) s;
line_breakcheck();
- if (did_echo_string_emsg) { // recursion error, bail out
- break;
- }
}
/* Allocate result buffer with its total size, avoid re-allocation and
@@ -5531,6 +5598,22 @@ static int list_join(garray_T *const gap, list_T *const l,
return retval;
}
+/// Get next (unique) copy ID
+///
+/// Used for traversing nested structures e.g. when serializing them or garbage
+/// collecting.
+int get_copyID(void)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ // CopyID for recursively traversing lists and dicts
+ //
+ // This value is needed to avoid endless recursiveness. Last bit is used for
+ // previous_funccal and normally ignored when comparing.
+ static int current_copyID = 0;
+ current_copyID += COPYID_INC;
+ return current_copyID;
+}
+
/*
* Garbage collection for lists and dictionaries.
*
@@ -5566,8 +5649,7 @@ bool garbage_collect(void)
// We advance by two because we add one for items referenced through
// previous_funccal.
- current_copyID += COPYID_INC;
- int copyID = current_copyID;
+ const int copyID = get_copyID();
// 1. Go through all accessible variables and mark all lists and dicts
// with copyID.
@@ -5912,6 +5994,15 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
}
break;
}
+
+ case VAR_FUNC:
+ case VAR_UNKNOWN:
+ case VAR_SPECIAL:
+ case VAR_FLOAT:
+ case VAR_NUMBER:
+ case VAR_STRING: {
+ break;
+ }
}
return abort;
}
@@ -5998,6 +6089,27 @@ static void rettv_dict_alloc(typval_T *rettv)
++d->dv_refcount;
}
+/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary.
+///
+/// @param d The Dictionary to clear
+void dict_clear(dict_T *d)
+ FUNC_ATTR_NONNULL_ALL
+{
+ hash_lock(&d->dv_hashtab);
+ assert(d->dv_hashtab.ht_locked > 0);
+
+ size_t todo = d->dv_hashtab.ht_used;
+ for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ dictitem_free(HI2DI(hi));
+ hash_remove(&d->dv_hashtab, hi);
+ todo--;
+ }
+ }
+
+ hash_unlock(&d->dv_hashtab);
+}
+
/*
* Unreference a Dictionary: decrement the reference count and free it when it
@@ -6239,6 +6351,24 @@ int dict_add_list(dict_T *d, char *key, list_T *list)
return OK;
}
+/// Set all existing keys in "dict" as read-only.
+///
+/// This does not protect against adding new keys to the Dictionary.
+///
+/// @param dict The dict whose keys should be frozen
+void dict_set_keys_readonly(dict_T *dict)
+ FUNC_ATTR_NONNULL_ALL
+{
+ size_t todo = dict->dv_hashtab.ht_used;
+ for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) {
+ if (HASHITEM_EMPTY(hi)) {
+ continue;
+ }
+ todo--;
+ HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
+ }
+}
+
/*
* Get the number of items in a Dictionary.
*/
@@ -6464,580 +6594,22 @@ failret:
return OK;
}
-#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \
- do { \
- if ((val)->copyID_attr == copyID) { \
- CONV_RECURSE((val), conv_type); \
- } \
- (val)->copyID_attr = copyID; \
- } while (0)
-
-/// Define functions which convert VimL value to something else
-///
-/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const
-/// tv)` which returns OK or FAIL and helper functions.
+/// Convert the string to a floating point number
///
-/// @param firstargtype Type of the first argument. It will be used to return
-/// the results.
-/// @param firstargname Name of the first argument.
-/// @param name Name of the target converter.
-#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \
-static int name##_convert_one_value(firstargtype firstargname, \
- MPConvStack *const mpstack, \
- typval_T *const tv, \
- const int copyID, \
- const char *const objname) \
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
-{ \
- switch (tv->v_type) { \
- case VAR_STRING: { \
- CONV_STRING(tv->vval.v_string, STRLEN(tv->vval.v_string)); \
- break; \
- } \
- case VAR_NUMBER: { \
- CONV_NUMBER(tv->vval.v_number); \
- break; \
- } \
- case VAR_FLOAT: { \
- CONV_FLOAT(tv->vval.v_float); \
- break; \
- } \
- case VAR_FUNC: { \
- CONV_FUNC(tv->vval.v_string); \
- break; \
- } \
- case VAR_LIST: { \
- if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
- CONV_EMPTY_LIST(); \
- break; \
- } \
- CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, kMPConvList); \
- CONV_LIST_START(tv->vval.v_list); \
- kv_push( \
- MPConvStackVal, \
- *mpstack, \
- ((MPConvStackVal) { \
- .type = kMPConvList, \
- .data = { \
- .l = { \
- .list = tv->vval.v_list, \
- .li = tv->vval.v_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case VAR_DICT: { \
- if (tv->vval.v_dict == NULL \
- || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \
- CONV_EMPTY_DICT(); \
- break; \
- } \
- const dictitem_T *type_di; \
- const dictitem_T *val_di; \
- if (CONV_ALLOW_SPECIAL \
- && tv->vval.v_dict->dv_hashtab.ht_used == 2 \
- && (type_di = dict_find((dict_T *) tv->vval.v_dict, \
- (char_u *) "_TYPE", -1)) != NULL \
- && type_di->di_tv.v_type == VAR_LIST \
- && (val_di = dict_find((dict_T *) tv->vval.v_dict, \
- (char_u *) "_VAL", -1)) != NULL) { \
- size_t i; \
- for (i = 0; i < ARRAY_SIZE(msgpack_type_lists); i++) { \
- if (type_di->di_tv.vval.v_list == msgpack_type_lists[i]) { \
- break; \
- } \
- } \
- if (i == ARRAY_SIZE(msgpack_type_lists)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- switch ((MessagePackType) i) { \
- case kMPNil: { \
- CONV_SPECIAL_NIL(); \
- break; \
- } \
- case kMPBoolean: { \
- if (val_di->di_tv.v_type != VAR_NUMBER) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- CONV_SPECIAL_BOOL(val_di->di_tv.vval.v_number); \
- break; \
- } \
- case kMPInteger: { \
- const list_T *val_list; \
- varnumber_T sign; \
- varnumber_T highest_bits; \
- varnumber_T high_bits; \
- varnumber_T low_bits; \
- /* List of 4 integers; first is signed (should be 1 or -1, but */ \
- /* this is not checked), second is unsigned and have at most */ \
- /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \
- /* bits is not checked), other unsigned and have at most 31 */ \
- /* non-zero bits (number of bits is not checked).*/ \
- if (val_di->di_tv.v_type != VAR_LIST \
- || (val_list = val_di->di_tv.vval.v_list) == NULL \
- || val_list->lv_len != 4 \
- || val_list->lv_first->li_tv.v_type != VAR_NUMBER \
- || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \
- || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \
- || (highest_bits = \
- val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \
- || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \
- || (high_bits = \
- val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \
- || val_list->lv_last->li_tv.v_type != VAR_NUMBER \
- || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \
- | (uint64_t) (((uint64_t) high_bits) << 31) \
- | (uint64_t) low_bits); \
- if (sign > 0) { \
- CONV_UNSIGNED_NUMBER(number); \
- } else { \
- CONV_NUMBER(-number); \
- } \
- break; \
- } \
- case kMPFloat: { \
- if (val_di->di_tv.v_type != VAR_FLOAT) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- CONV_FLOAT(val_di->di_tv.vval.v_float); \
- break; \
- } \
- case kMPString: \
- case kMPBinary: { \
- const bool is_string = ((MessagePackType) i == kMPString); \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- size_t len; \
- char *buf; \
- if (!vim_list_to_buf(val_di->di_tv.vval.v_list, &len, &buf)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- if (is_string) { \
- CONV_STR_STRING(buf, len); \
- } else { \
- CONV_STRING(buf, len); \
- } \
- xfree(buf); \
- break; \
- } \
- case kMPArray: { \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, \
- kMPConvList); \
- CONV_LIST_START(val_di->di_tv.vval.v_list); \
- kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
- .type = kMPConvList, \
- .data = { \
- .l = { \
- .list = val_di->di_tv.vval.v_list, \
- .li = val_di->di_tv.vval.v_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case kMPMap: { \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- if (val_di->di_tv.vval.v_list == NULL) { \
- CONV_EMPTY_DICT(); \
- break; \
- } \
- list_T *const val_list = val_di->di_tv.vval.v_list; \
- for (const listitem_T *li = val_list->lv_first; li != NULL; \
- li = li->li_next) { \
- if (li->li_tv.v_type != VAR_LIST \
- || li->li_tv.vval.v_list->lv_len != 2) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- } \
- CHECK_SELF_REFERENCE(val_list, lv_copyID, kMPConvPairs); \
- CONV_SPECIAL_MAP_START(val_list); \
- kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
- .type = kMPConvPairs, \
- .data = { \
- .l = { \
- .list = val_list, \
- .li = val_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case kMPExt: { \
- const list_T *val_list; \
- varnumber_T type; \
- if (val_di->di_tv.v_type != VAR_LIST \
- || (val_list = val_di->di_tv.vval.v_list) == NULL \
- || val_list->lv_len != 2 \
- || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \
- || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \
- || type < INT8_MIN \
- || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- size_t len; \
- char *buf; \
- if (!vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \
- &len, &buf)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- CONV_EXT_STRING(buf, len, type); \
- xfree(buf); \
- break; \
- } \
- } \
- break; \
- } \
-name##_convert_one_value_regular_dict: \
- CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, kMPConvDict); \
- CONV_DICT_START(tv->vval.v_dict); \
- kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
- .type = kMPConvDict, \
- .data = { \
- .d = { \
- .dict = tv->vval.v_dict, \
- .hi = tv->vval.v_dict->dv_hashtab.ht_array, \
- .todo = tv->vval.v_dict->dv_hashtab.ht_used, \
- }, \
- }, \
- })); \
- break; \
- } \
- default: { \
- EMSG2(_(e_intern2), #name "_convert_one_value()"); \
- return FAIL; \
- } \
- } \
- return OK; \
-} \
-\
-scope int vim_to_##name(firstargtype firstargname, typval_T *const tv, \
- const char *const objname) \
- FUNC_ATTR_WARN_UNUSED_RESULT \
-{ \
- current_copyID += COPYID_INC; \
- const int copyID = current_copyID; \
- MPConvStack mpstack; \
- kv_init(mpstack); \
- if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \
- == FAIL) { \
- goto vim_to_msgpack_error_ret; \
- } \
- while (kv_size(mpstack)) { \
- MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \
- typval_T *cur_tv = NULL; \
- switch (cur_mpsv->type) { \
- case kMPConvDict: { \
- if (!cur_mpsv->data.d.todo) { \
- (void) kv_pop(mpstack); \
- cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \
- CONV_DICT_END(cur_mpsv->data.d.dict); \
- continue; \
- } else if (cur_mpsv->data.d.todo \
- != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \
- CONV_DICT_BETWEEN_ITEMS(cur_mpsv->data.d.dict); \
- } \
- while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \
- cur_mpsv->data.d.hi++; \
- } \
- dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \
- cur_mpsv->data.d.todo--; \
- cur_mpsv->data.d.hi++; \
- CONV_STR_STRING(&di->di_key[0], STRLEN(&di->di_key[0])); \
- CONV_DICT_AFTER_KEY(cur_mpsv->data.d.dict); \
- cur_tv = &di->di_tv; \
- break; \
- } \
- case kMPConvList: { \
- if (cur_mpsv->data.l.li == NULL) { \
- (void) kv_pop(mpstack); \
- cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
- CONV_LIST_END(cur_mpsv->data.l.list); \
- continue; \
- } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
- CONV_LIST_BETWEEN_ITEMS(cur_mpsv->data.l.list); \
- } \
- cur_tv = &cur_mpsv->data.l.li->li_tv; \
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
- break; \
- } \
- case kMPConvPairs: { \
- if (cur_mpsv->data.l.li == NULL) { \
- (void) kv_pop(mpstack); \
- cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
- continue; \
- } \
- const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \
- if (name##_convert_one_value(firstargname, &mpstack, \
- &kv_pair->lv_first->li_tv, copyID, \
- objname) == FAIL) { \
- goto vim_to_msgpack_error_ret; \
- } \
- cur_tv = &kv_pair->lv_last->li_tv; \
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
- break; \
- } \
- } \
- if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \
- objname) == FAIL) { \
- goto vim_to_msgpack_error_ret; \
- } \
- } \
- kv_destroy(mpstack); \
- return OK; \
-vim_to_msgpack_error_ret: \
- kv_destroy(mpstack); \
- return FAIL; \
-}
-
-#define CONV_STRING(buf, len) \
- do { \
- const char *const buf_ = (const char *) buf; \
- if (buf == NULL) { \
- ga_concat(gap, (char_u *) "''"); \
- } else { \
- const size_t len_ = (len); \
- size_t num_quotes = 0; \
- for (size_t i = 0; i < len_; i++) { \
- if (buf_[i] == '\'') { \
- num_quotes++; \
- } \
- } \
- ga_grow(gap, 2 + len_ + num_quotes); \
- ga_append(gap, '\''); \
- for (size_t i = 0; i < len_; i++) { \
- if (buf_[i] == '\'') { \
- num_quotes++; \
- ga_append(gap, '\''); \
- } \
- ga_append(gap, buf_[i]); \
- } \
- ga_append(gap, '\''); \
- } \
- } while (0)
-
-#define CONV_STR_STRING(buf, len) \
- CONV_STRING(buf, len)
-
-#define CONV_EXT_STRING(buf, len, type)
-
-#define CONV_NUMBER(num) \
- do { \
- char numbuf[NUMBUFLEN]; \
- vim_snprintf(numbuf, NUMBUFLEN - 1, "%" PRId64, (int64_t) (num)); \
- ga_concat(gap, (char_u *) numbuf); \
- } while (0)
-
-#define CONV_FLOAT(flt) \
- do { \
- char numbuf[NUMBUFLEN]; \
- vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", (flt)); \
- ga_concat(gap, (char_u *) numbuf); \
- } while (0)
-
-#define CONV_FUNC(fun) \
- do { \
- ga_concat(gap, (char_u *) "function("); \
- CONV_STRING(fun, STRLEN(fun)); \
- ga_append(gap, ')'); \
- } while (0)
-
-#define CONV_EMPTY_LIST() \
- ga_concat(gap, (char_u *) "[]")
-
-#define CONV_LIST_START(lst) \
- ga_append(gap, '[')
-
-#define CONV_EMPTY_DICT() \
- ga_concat(gap, (char_u *) "{}")
-
-#define CONV_SPECIAL_NIL()
-
-#define CONV_SPECIAL_BOOL(num)
-
-#define CONV_UNSIGNED_NUMBER(num)
-
-#define CONV_SPECIAL_MAP_START(lst)
-
-#define CONV_DICT_START(dct) \
- ga_append(gap, '{')
-
-#define CONV_DICT_END(dct) \
- ga_append(gap, '}')
-
-#define CONV_DICT_AFTER_KEY(dct) \
- ga_concat(gap, (char_u *) ": ")
-
-#define CONV_DICT_BETWEEN_ITEMS(dct) \
- ga_concat(gap, (char_u *) ", ")
-
-#define CONV_LIST_END(lst) \
- ga_append(gap, ']')
-
-#define CONV_LIST_BETWEEN_ITEMS(lst) \
- CONV_DICT_BETWEEN_ITEMS(NULL)
-
-#define CONV_RECURSE(val, conv_type) \
- do { \
- if (!did_echo_string_emsg) { \
- /* Only give this message once for a recursive call to avoid */ \
- /* flooding the user with errors. */ \
- did_echo_string_emsg = true; \
- EMSG(_("E724: unable to correctly dump variable " \
- "with self-referencing container")); \
- } \
- char ebuf[NUMBUFLEN + 7]; \
- size_t backref = 0; \
- for (; backref < kv_size(*mpstack); backref++) { \
- const MPConvStackVal mpval = kv_a(MPConvStackVal, *mpstack, backref); \
- if (mpval.type == conv_type) { \
- if (conv_type == kMPConvDict) { \
- if ((void *) mpval.data.d.dict == (void *) (val)) { \
- break; \
- } \
- } else if (conv_type == kMPConvList) { \
- if ((void *) mpval.data.l.list == (void *) (val)) { \
- break; \
- } \
- } \
- } \
- } \
- vim_snprintf(ebuf, NUMBUFLEN + 6, "{E724@%zu}", backref); \
- ga_concat(gap, (char_u *) &ebuf[0]); \
- return OK; \
- } while (0)
-
-#define CONV_ALLOW_SPECIAL false
-
-DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap)
-
-#undef CONV_RECURSE
-#define CONV_RECURSE(val, conv_type) \
- do { \
- char ebuf[NUMBUFLEN + 7]; \
- size_t backref = 0; \
- for (; backref < kv_size(*mpstack); backref++) { \
- const MPConvStackVal mpval = kv_a(MPConvStackVal, *mpstack, backref); \
- if (mpval.type == conv_type) { \
- if (conv_type == kMPConvDict) { \
- if ((void *) mpval.data.d.dict == (void *) val) { \
- break; \
- } \
- } else if (conv_type == kMPConvList) { \
- if ((void *) mpval.data.l.list == (void *) val) { \
- break; \
- } \
- } \
- } \
- } \
- if (conv_type == kMPConvDict) { \
- vim_snprintf(ebuf, NUMBUFLEN + 6, "{...@%zu}", backref); \
- } else { \
- vim_snprintf(ebuf, NUMBUFLEN + 6, "[...@%zu]", backref); \
- } \
- ga_concat(gap, (char_u *) &ebuf[0]); \
- return OK; \
- } while (0)
-
-DEFINE_VIML_CONV_FUNCTIONS(static, echo, garray_T *const, gap)
-
-#undef CONV_STRING
-#undef CONV_STR_STRING
-#undef CONV_EXT_STRING
-#undef CONV_NUMBER
-#undef CONV_FLOAT
-#undef CONV_FUNC
-#undef CONV_EMPTY_LIST
-#undef CONV_LIST_START
-#undef CONV_EMPTY_DICT
-#undef CONV_SPECIAL_NIL
-#undef CONV_SPECIAL_BOOL
-#undef CONV_UNSIGNED_NUMBER
-#undef CONV_SPECIAL_MAP_START
-#undef CONV_DICT_START
-#undef CONV_DICT_END
-#undef CONV_DICT_AFTER_KEY
-#undef CONV_DICT_BETWEEN_ITEMS
-#undef CONV_LIST_END
-#undef CONV_LIST_BETWEEN_ITEMS
-#undef CONV_RECURSE
-#undef CONV_ALLOW_SPECIAL
-
-/// Return a string with the string representation of a variable.
-/// Puts quotes around strings, so that they can be parsed back by eval().
+/// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to
+/// make sure this always uses a decimal point.
///
-/// @param[in] tv typval_T to convert.
-/// @param[out] len Location where length of the result will be saved.
+/// @param[in] text String to convert.
+/// @param[out] ret_value Location where conversion result is saved.
///
-/// @return String representation of the variable or NULL.
-static char *tv2string(typval_T *tv, size_t *len)
- FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
-{
- garray_T ga;
- ga_init(&ga, (int)sizeof(char), 80);
- vim_to_string(&ga, tv, "tv2string() argument");
- did_echo_string_emsg = false;
- if (len != NULL) {
- *len = (size_t) ga.ga_len;
- }
- ga_append(&ga, '\0');
- return (char *) ga.ga_data;
-}
-
-/// Return a string with the string representation of a variable.
-/// Does not put quotes around strings, as ":echo" displays values.
-///
-/// @param[in] tv typval_T to convert.
-/// @param[out] len Location where length of the result will be saved.
-///
-/// @return String representation of the variable or NULL.
-static char *echo_string(typval_T *tv, size_t *len)
- FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
-{
- garray_T ga;
- ga_init(&ga, (int)sizeof(char), 80);
- if (tv->v_type == VAR_STRING || tv->v_type == VAR_FUNC) {
- if (tv->vval.v_string != NULL) {
- ga_concat(&ga, tv->vval.v_string);
- }
- } else {
- vim_to_echo(&ga, tv, ":echo argument");
- did_echo_string_emsg = false;
- }
- if (len != NULL) {
- *len = (size_t) ga.ga_len;
- }
- ga_append(&ga, '\0');
- return (char *) ga.ga_data;
-}
-
-/*
- * Convert the string "text" to a floating point number.
- * This uses strtod(). setlocale(LC_NUMERIC, "C") has been used to make sure
- * this always uses a decimal point.
- * Returns the length of the text that was consumed.
- */
-static int
-string2float (
- char_u *text,
- float_T *value /* result stored here */
-)
+/// @return Length of the text that was consumed.
+size_t string2float(const char *const text, float_T *const ret_value)
+ FUNC_ATTR_NONNULL_ALL
{
- char *s = (char *)text;
- float_T f;
+ char *s = NULL;
- f = strtod(s, &s);
- *value = f;
- return (int)((char_u *)s - text);
+ *ret_value = strtod(text, &s);
+ return (size_t) (s - text);
}
/// Get the value of an environment variable.
@@ -7097,7 +6669,7 @@ static struct fst {
} functions[] =
{
{ "abs", 1, 1, f_abs },
- { "acos", 1, 1, f_acos }, // WJMc
+ { "acos", 1, 1, f_acos }, // WJMc
{ "add", 2, 2, f_add },
{ "and", 2, 2, f_and },
{ "append", 2, 2, f_append },
@@ -7107,6 +6679,8 @@ static struct fst {
{ "argv", 0, 1, f_argv },
{ "asin", 1, 1, f_asin }, // WJMc
{ "assert_equal", 2, 3, f_assert_equal },
+ { "assert_exception", 1, 2, f_assert_exception },
+ { "assert_fails", 1, 2, f_assert_fails },
{ "assert_false", 1, 2, f_assert_false },
{ "assert_true", 1, 2, f_assert_true },
{ "atan", 1, 1, f_atan },
@@ -7114,9 +6688,9 @@ static struct fst {
{ "browse", 4, 4, f_browse },
{ "browsedir", 2, 2, f_browsedir },
{ "bufexists", 1, 1, f_bufexists },
- { "buffer_exists", 1, 1, f_bufexists }, // obsolete
- { "buffer_name", 1, 1, f_bufname }, // obsolete
- { "buffer_number", 1, 1, f_bufnr }, // obsolete
+ { "buffer_exists", 1, 1, f_bufexists }, // obsolete
+ { "buffer_name", 1, 1, f_bufname }, // obsolete
+ { "buffer_number", 1, 1, f_bufnr }, // obsolete
{ "buflisted", 1, 1, f_buflisted },
{ "bufloaded", 1, 1, f_bufloaded },
{ "bufname", 1, 1, f_bufname },
@@ -7143,7 +6717,7 @@ static struct fst {
{ "cscope_connection", 0, 3, f_cscope_connection },
{ "cursor", 1, 3, f_cursor },
{ "deepcopy", 1, 2, f_deepcopy },
- { "delete", 1, 1, f_delete },
+ { "delete", 1, 2, f_delete },
{ "dictwatcheradd", 3, 3, f_dictwatcheradd },
{ "dictwatcherdel", 3, 3, f_dictwatcherdel },
{ "did_filetype", 0, 0, f_did_filetype },
@@ -7160,7 +6734,7 @@ static struct fst {
{ "expand", 1, 3, f_expand },
{ "extend", 2, 3, f_extend },
{ "feedkeys", 1, 2, f_feedkeys },
- { "file_readable", 1, 1, f_filereadable }, // obsolete
+ { "file_readable", 1, 1, f_filereadable }, // obsolete
{ "filereadable", 1, 1, f_filereadable },
{ "filewritable", 1, 1, f_filewritable },
{ "filter", 2, 2, f_filter },
@@ -7190,7 +6764,7 @@ static struct fst {
{ "getcmdtype", 0, 0, f_getcmdtype },
{ "getcmdwintype", 0, 0, f_getcmdwintype },
{ "getcurpos", 0, 0, f_getcurpos },
- { "getcwd", 0, 0, f_getcwd },
+ { "getcwd", 0, 2, f_getcwd },
{ "getfontname", 0, 1, f_getfontname },
{ "getfperm", 1, 1, f_getfperm },
{ "getfsize", 1, 1, f_getfsize },
@@ -7214,7 +6788,7 @@ static struct fst {
{ "globpath", 2, 5, f_globpath },
{ "has", 1, 1, f_has },
{ "has_key", 2, 2, f_has_key },
- { "haslocaldir", 0, 0, f_haslocaldir },
+ { "haslocaldir", 0, 2, f_haslocaldir },
{ "hasmapto", 1, 3, f_hasmapto },
{ "highlightID", 1, 1, f_hlID }, // obsolete
{ "highlight_exists", 1, 1, f_hlexists }, // obsolete
@@ -7247,6 +6821,8 @@ static struct fst {
{ "jobstop", 1, 1, f_jobstop },
{ "jobwait", 1, 2, f_jobwait },
{ "join", 1, 2, f_join },
+ { "json_decode", 1, 1, f_json_decode },
+ { "json_encode", 1, 1, f_json_encode },
{ "keys", 1, 1, f_keys },
{ "last_buffer_nr", 0, 0, f_last_buffer_nr }, // obsolete
{ "len", 1, 1, f_len },
@@ -7262,8 +6838,8 @@ static struct fst {
{ "maparg", 1, 4, f_maparg },
{ "mapcheck", 1, 3, f_mapcheck },
{ "match", 2, 4, f_match },
- { "matchadd", 2, 4, f_matchadd },
- { "matchaddpos", 2, 4, f_matchaddpos },
+ { "matchadd", 2, 5, f_matchadd },
+ { "matchaddpos", 2, 5, f_matchaddpos },
{ "matcharg", 1, 1, f_matcharg },
{ "matchdelete", 1, 1, f_matchdelete },
{ "matchend", 2, 4, f_matchend },
@@ -7281,13 +6857,14 @@ static struct fst {
{ "pathshorten", 1, 1, f_pathshorten },
{ "pow", 2, 2, f_pow },
{ "prevnonblank", 1, 1, f_prevnonblank },
- { "printf", 2, 19, f_printf },
+ { "printf", 2, MAX_FUNC_ARGS, f_printf },
{ "pumvisible", 0, 0, f_pumvisible },
{ "py3eval", 1, 1, f_py3eval },
{ "pyeval", 1, 1, f_pyeval },
{ "range", 1, 3, f_range },
{ "readfile", 1, 3, f_readfile },
{ "reltime", 0, 2, f_reltime },
+ { "reltimefloat", 1, 1, f_reltimefloat },
{ "reltimestr", 1, 1, f_reltimestr },
{ "remove", 2, 3, f_remove },
{ "rename", 2, 2, f_rename },
@@ -7295,8 +6872,8 @@ static struct fst {
{ "resolve", 1, 1, f_resolve },
{ "reverse", 1, 1, f_reverse },
{ "round", 1, 1, f_round },
- { "rpcnotify", 2, 64, f_rpcnotify },
- { "rpcrequest", 2, 64, f_rpcrequest },
+ { "rpcnotify", 2, MAX_FUNC_ARGS, f_rpcnotify },
+ { "rpcrequest", 2, MAX_FUNC_ARGS, f_rpcrequest },
{ "rpcstart", 1, 2, f_rpcstart },
{ "rpcstop", 1, 1, f_rpcstop },
{ "screenattr", 2, 2, f_screenattr },
@@ -7314,11 +6891,12 @@ static struct fst {
{ "setbufvar", 3, 3, f_setbufvar },
{ "setcharsearch", 1, 1, f_setcharsearch },
{ "setcmdpos", 1, 1, f_setcmdpos },
+ { "setfperm", 2, 2, f_setfperm },
{ "setline", 2, 2, f_setline },
- { "setloclist", 2, 3, f_setloclist },
+ { "setloclist", 2, 4, f_setloclist },
{ "setmatches", 1, 1, f_setmatches },
{ "setpos", 2, 2, f_setpos },
- { "setqflist", 1, 2, f_setqflist },
+ { "setqflist", 1, 3, f_setqflist },
{ "setreg", 2, 3, f_setreg },
{ "settabvar", 3, 3, f_settabvar },
{ "settabwinvar", 4, 4, f_settabwinvar },
@@ -7337,7 +6915,7 @@ static struct fst {
{ "sqrt", 1, 1, f_sqrt },
{ "str2float", 1, 1, f_str2float },
{ "str2nr", 1, 2, f_str2nr },
- { "strchars", 1, 1, f_strchars },
+ { "strchars", 1, 2, f_strchars },
{ "strdisplaywidth", 1, 2, f_strdisplaywidth },
{ "strftime", 1, 2, f_strftime },
{ "stridx", 2, 3, f_stridx },
@@ -7366,6 +6944,8 @@ static struct fst {
{ "tempname", 0, 0, f_tempname },
{ "termopen", 1, 2, f_termopen },
{ "test", 1, 1, f_test },
+ { "timer_start", 2, 3, f_timer_start },
+ { "timer_stop", 1, 1, f_timer_stop },
{ "tolower", 1, 1, f_tolower },
{ "toupper", 1, 1, f_toupper },
{ "tr", 3, 3, f_tr },
@@ -7387,6 +6967,7 @@ static struct fst {
{ "winrestview", 1, 1, f_winrestview },
{ "winsaveview", 0, 0, f_winsaveview },
{ "winwidth", 1, 1, f_winwidth },
+ { "wordcount", 0, 0, f_wordcount },
{ "writefile", 2, 3, f_writefile },
{ "xor", 2, 2, f_xor },
};
@@ -7855,7 +7436,8 @@ static void f_add(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = 1; /* Default: Failed */
if (argvars[0].v_type == VAR_LIST) {
if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock, (char_u *)_("add() argument"))) {
+ && !tv_check_lock(l->lv_lock,
+ (char_u *)N_("add() argument"), true)) {
list_append_tv(l, &argvars[1]);
copy_tv(&argvars[0], rettv);
}
@@ -8020,19 +7602,19 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv,
char_u *tofree;
if (opt_msg_tv->v_type != VAR_UNKNOWN) {
- tofree = (char_u *) tv2string(opt_msg_tv, NULL);
+ tofree = (char_u *) encode_tv2string(opt_msg_tv, NULL);
ga_concat(gap, tofree);
xfree(tofree);
} else {
ga_concat(gap, (char_u *)"Expected ");
if (exp_str == NULL) {
- tofree = (char_u *) tv2string(exp_tv, NULL);
+ tofree = (char_u *) encode_tv2string(exp_tv, NULL);
ga_concat(gap, tofree);
xfree(tofree);
} else {
ga_concat(gap, exp_str);
}
- tofree = (char_u *) tv2string(got_tv, NULL);
+ tofree = (char_u *) encode_tv2string(got_tv, NULL);
ga_concat(gap, (char_u *)" but got ");
ga_concat(gap, tofree);
xfree(tofree);
@@ -8066,17 +7648,80 @@ static void f_assert_equal(typval_T *argvars, typval_T *rettv)
}
}
+/// "assert_exception(string[, msg])" function
+static void f_assert_exception(typval_T *argvars, typval_T *rettv)
+{
+ garray_T ga;
+
+ char *error = (char *)get_tv_string_chk(&argvars[0]);
+ if (vimvars[VV_EXCEPTION].vv_str == NULL) {
+ prepare_assert_error(&ga);
+ ga_concat(&ga, (char_u *)"v:exception is not set");
+ assert_error(&ga);
+ ga_clear(&ga);
+ } else if (strstr((char *)vimvars[VV_EXCEPTION].vv_str, error) == NULL) {
+ prepare_assert_error(&ga);
+ fill_assert_error(&ga, &argvars[1], NULL, &argvars[0],
+ &vimvars[VV_EXCEPTION].vv_tv);
+ assert_error(&ga);
+ ga_clear(&ga);
+ }
+}
+
+/// "assert_fails(cmd [, error])" function
+static void f_assert_fails(typval_T *argvars, typval_T *rettv)
+{
+ char_u *cmd = get_tv_string_chk(&argvars[0]);
+ garray_T ga;
+
+ called_emsg = false;
+ suppress_errthrow = true;
+ emsg_silent = true;
+ do_cmdline_cmd((char *)cmd);
+ if (!called_emsg) {
+ prepare_assert_error(&ga);
+ ga_concat(&ga, (char_u *)"command did not fail: ");
+ ga_concat(&ga, cmd);
+ assert_error(&ga);
+ ga_clear(&ga);
+ } else if (argvars[1].v_type != VAR_UNKNOWN) {
+ char_u buf[NUMBUFLEN];
+ char *error = (char *)get_tv_string_buf_chk(&argvars[1], buf);
+
+ if (error == NULL
+ || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) {
+ prepare_assert_error(&ga);
+ fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
+ &vimvars[VV_ERRMSG].vv_tv);
+ assert_error(&ga);
+ ga_clear(&ga);
+ }
+ }
+
+ called_emsg = false;
+ suppress_errthrow = false;
+ emsg_silent = false;
+ emsg_on_display = false;
+ set_vim_var_string(VV_ERRMSG, NULL, 0);
+}
+
// Common for assert_true() and assert_false().
-static void assert_bool(typval_T *argvars, bool isTrue)
+static void assert_bool(typval_T *argvars, bool is_true)
{
int error = (int)false;
garray_T ga;
- if (argvars[0].v_type != VAR_NUMBER ||
- (get_tv_number_chk(&argvars[0], &error) == 0) == isTrue || error) {
+ if ((argvars[0].v_type != VAR_NUMBER
+ || (get_tv_number_chk(&argvars[0], &error) == 0) == is_true
+ || error)
+ && (argvars[0].v_type != VAR_SPECIAL
+ || (argvars[0].vval.v_special
+ != (SpecialVarValue) (is_true
+ ? kSpecialVarTrue
+ : kSpecialVarFalse)))) {
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[1],
- (char_u *)(isTrue ? "True" : "False"),
+ (char_u *)(is_true ? "True" : "False"),
NULL, &argvars[0]);
assert_error(&ga);
ga_clear(&ga);
@@ -8738,16 +8383,17 @@ static void f_cscope_connection(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = cs_connection(num, dbpath, prepend);
}
-/*
- * "cursor(lnum, col)" function
- *
- * Moves the cursor to the specified line and column.
- * Returns 0 when the position could be set, -1 otherwise.
- */
+/// "cursor(lnum, col)" function, or
+/// "cursor(list)"
+///
+/// Moves the cursor to the specified line and column.
+///
+/// @returns 0 when the position could be set, -1 otherwise.
static void f_cursor(typval_T *argvars, typval_T *rettv)
{
long line, col;
long coladd = 0;
+ bool set_curswant = true;
rettv->vval.v_number = -1;
if (argvars[1].v_type == VAR_UNKNOWN) {
@@ -8755,37 +8401,44 @@ static void f_cursor(typval_T *argvars, typval_T *rettv)
colnr_T curswant = -1;
if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) {
+ EMSG(_(e_invarg));
return;
}
+
line = pos.lnum;
col = pos.col;
coladd = pos.coladd;
if (curswant >= 0) {
curwin->w_curswant = curswant - 1;
+ set_curswant = false;
}
} else {
line = get_tv_lnum(argvars);
col = get_tv_number_chk(&argvars[1], NULL);
- if (argvars[2].v_type != VAR_UNKNOWN)
+ if (argvars[2].v_type != VAR_UNKNOWN) {
coladd = get_tv_number_chk(&argvars[2], NULL);
+ }
}
if (line < 0 || col < 0
- || coladd < 0
- )
- return; /* type error; errmsg already given */
- if (line > 0)
+ || coladd < 0) {
+ return; // type error; errmsg already given
+ }
+ if (line > 0) {
curwin->w_cursor.lnum = line;
- if (col > 0)
+ }
+ if (col > 0) {
curwin->w_cursor.col = col - 1;
+ }
curwin->w_cursor.coladd = coladd;
- /* Make sure the cursor is in a valid position. */
+ // Make sure the cursor is in a valid position.
check_cursor();
- /* Correct cursor for multi-byte character. */
- if (has_mbyte)
+ // Correct cursor for multi-byte character.
+ if (has_mbyte) {
mb_adjust_cursor();
+ }
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = set_curswant;
rettv->vval.v_number = 0;
}
@@ -8798,25 +8451,51 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN)
noref = get_tv_number_chk(&argvars[1], NULL);
- if (noref < 0 || noref > 1)
+ if (noref < 0 || noref > 1) {
EMSG(_(e_invarg));
- else {
- current_copyID += COPYID_INC;
+ } else {
var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0
- ? current_copyID
+ ? get_copyID()
: 0));
}
}
-/*
- * "delete()" function
- */
+// "delete()" function
static void f_delete(typval_T *argvars, typval_T *rettv)
{
- if (check_restricted() || check_secure())
- rettv->vval.v_number = -1;
- else
- rettv->vval.v_number = os_remove((char *)get_tv_string(&argvars[0]));
+ char_u nbuf[NUMBUFLEN];
+ char_u *name;
+ char_u *flags;
+
+ rettv->vval.v_number = -1;
+ if (check_restricted() || check_secure()) {
+ return;
+ }
+
+ name = get_tv_string(&argvars[0]);
+ if (name == NULL || *name == NUL) {
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ flags = get_tv_string_buf(&argvars[1], nbuf);
+ } else {
+ flags = (char_u *)"";
+ }
+
+ if (*flags == NUL) {
+ // delete a file
+ rettv->vval.v_number = os_remove((char *)name) == 0 ? 0 : -1;
+ } else if (STRCMP(flags, "d") == 0) {
+ // delete an empty directory
+ rettv->vval.v_number = os_rmdir((char *)name) == 0 ? 0 : -1;
+ } else if (STRCMP(flags, "rf") == 0) {
+ // delete a directory recursively
+ rettv->vval.v_number = delete_recursive(name);
+ } else {
+ EMSG2(_(e_invexpr2), flags);
+ }
}
// dictwatcheradd(dict, key, funcref) function
@@ -8993,7 +8672,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv)
*/
static void f_empty(typval_T *argvars, typval_T *rettv)
{
- int n;
+ bool n = true;
switch (argvars[0].v_type) {
case VAR_STRING:
@@ -9015,9 +8694,12 @@ static void f_empty(typval_T *argvars, typval_T *rettv)
n = argvars[0].vval.v_dict == NULL
|| argvars[0].vval.v_dict->dv_hashtab.ht_used == 0;
break;
- default:
- EMSG2(_(e_intern2), "f_empty()");
- n = 0;
+ case VAR_SPECIAL:
+ n = argvars[0].vval.v_special != kSpecialVarTrue;
+ break;
+ case VAR_UNKNOWN:
+ EMSG2(_(e_intern2), "f_empty(UNKNOWN)");
+ break;
}
rettv->vval.v_number = n;
@@ -9072,7 +8754,11 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv)
*/
static void f_executable(typval_T *argvars, typval_T *rettv)
{
- rettv->vval.v_number = os_can_exe(get_tv_string(&argvars[0]), NULL);
+ char_u *name = get_tv_string(&argvars[0]);
+
+ // Check in $PATH and also check directly if there is a directory name
+ rettv->vval.v_number = os_can_exe(name, NULL, true)
+ || (gettail_dir(name) != name && os_can_exe(name, NULL, false));
}
/// "exepath()" function
@@ -9081,7 +8767,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv)
char_u *arg = get_tv_string(&argvars[0]);
char_u *path = NULL;
- (void)os_can_exe(arg, &path);
+ (void)os_can_exe(arg, &path, true);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = path;
@@ -9130,9 +8816,10 @@ static void f_exists(typval_T *argvars, typval_T *rettv)
name = p;
len = get_name_len(&p, &tofree, TRUE, FALSE);
if (len > 0) {
- if (tofree != NULL)
+ if (tofree != NULL) {
name = tofree;
- n = (get_var_tv(name, len, &tv, FALSE, TRUE) == OK);
+ }
+ n = (get_var_tv(name, len, &tv, NULL, false, true) == OK);
if (n) {
/* handle d.key, l[idx], f(expr) */
n = (handle_subscript(&p, &tv, TRUE, FALSE) == OK);
@@ -9230,7 +8917,7 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action)
hashitem_T *hi2;
int todo;
bool watched = is_watched(d1);
- char *arg_errmsg = N_("extend() argument");
+ char_u *arg_errmsg = (char_u *)N_("extend() argument");
todo = (int)d2->dv_hashtab.ht_used;
for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) {
@@ -9264,8 +8951,8 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action)
} else if (*action == 'f' && HI2DI(hi2) != di1) {
typval_T oldtv;
- if (tv_check_lock(di1->di_tv.v_lock, (char_u *)_(arg_errmsg))
- || var_check_ro(di1->di_flags, (char_u *)_(arg_errmsg))) {
+ if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, true)
+ || var_check_ro(di1->di_flags, arg_errmsg, true)) {
break;
}
@@ -9291,7 +8978,7 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action)
*/
static void f_extend(typval_T *argvars, typval_T *rettv)
{
- char *arg_errmsg = N_("extend() argument");
+ char_u *arg_errmsg = (char_u *)N_("extend() argument");
if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) {
list_T *l1, *l2;
@@ -9301,7 +8988,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv)
l1 = argvars[0].vval.v_list;
l2 = argvars[1].vval.v_list;
- if (l1 != NULL && !tv_check_lock(l1->lv_lock, (char_u *)_(arg_errmsg))
+ if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, true)
&& l2 != NULL) {
if (argvars[2].v_type != VAR_UNKNOWN) {
before = get_tv_number_chk(&argvars[2], &error);
@@ -9331,7 +9018,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv)
d1 = argvars[0].vval.v_dict;
d2 = argvars[1].vval.v_dict;
- if (d1 != NULL && !tv_check_lock(d1->dv_lock, (char_u *)_(arg_errmsg))
+ if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, true)
&& d2 != NULL) {
/* Check the third argument. */
if (argvars[2].v_type != VAR_UNKNOWN) {
@@ -9477,19 +9164,19 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
int rem;
int todo;
char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
- char *arg_errmsg = (map ? N_("map() argument")
- : N_("filter() argument"));
+ char_u *arg_errmsg = (char_u *)(map ? N_("map() argument")
+ : N_("filter() argument"));
int save_did_emsg;
int idx = 0;
if (argvars[0].v_type == VAR_LIST) {
if ((l = argvars[0].vval.v_list) == NULL
- || (!map && tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg)))) {
+ || (!map && tv_check_lock(l->lv_lock, arg_errmsg, true))) {
return;
}
} else if (argvars[0].v_type == VAR_DICT) {
if ((d = argvars[0].vval.v_dict) == NULL
- || (!map && tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg)))) {
+ || (!map && tv_check_lock(d->dv_lock, arg_errmsg, true))) {
return;
}
} else {
@@ -9523,8 +9210,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
di = HI2DI(hi);
if (map
- && (tv_check_lock(di->di_tv.v_lock, (char_u *)_(arg_errmsg))
- || var_check_ro(di->di_flags, (char_u *)_(arg_errmsg)))) {
+ && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, true)
+ || var_check_ro(di->di_flags, arg_errmsg, true))) {
break;
}
@@ -9534,8 +9221,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
if (r == FAIL || did_emsg)
break;
if (!map && rem) {
- if (var_check_fixed(di->di_flags, (char_u *)_(arg_errmsg))
- || var_check_ro(di->di_flags, (char_u *)_(arg_errmsg))) {
+ if (var_check_fixed(di->di_flags, arg_errmsg, true)
+ || var_check_ro(di->di_flags, arg_errmsg, true)) {
break;
}
dictitem_remove(d, di);
@@ -9547,7 +9234,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
vimvars[VV_KEY].vv_type = VAR_NUMBER;
for (li = l->lv_first; li != NULL; li = nli) {
- if (map && tv_check_lock(li->li_tv.v_lock, (char_u *)_(arg_errmsg))) {
+ if (map && tv_check_lock(li->li_tv.v_lock, arg_errmsg, true)) {
break;
}
nli = li->li_next;
@@ -10188,22 +9875,130 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string[0] = cmdwin_type;
}
-/*
- * "getcwd()" function
- */
+/// `getcwd([{win}[, {tab}]])` function
+///
+/// Every scope not specified implies the currently selected scope object.
+///
+/// @pre The arguments must be of type number.
+/// @pre There may not be more than two arguments.
+/// @pre An argument may not be -1 if preceding arguments are not all -1.
+///
+/// @post The return value will be a string.
static void f_getcwd(typval_T *argvars, typval_T *rettv)
{
- char_u *cwd;
+ // Possible scope of working directory to return.
+ CdScope scope = MIN_CD_SCOPE;
+
+ // Numbers of the scope objects (window, tab) we want the working directory
+ // of. A `-1` means to skip this scope, a `0` means the current object.
+ int scope_number[] = {
+ [kCdScopeWindow] = 0, // Number of window to look at.
+ [kCdScopeTab ] = 0, // Number of tab to look at.
+ };
+
+ char_u *cwd = NULL; // Current working directory to print
+ char_u *from = NULL; // The original string to copy
+
+ tabpage_T *tp = curtab; // The tabpage to look at.
+ win_T *win = curwin; // The window to look at.
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
+
+ // Pre-conditions and scope extraction together
+ for (int i = MIN_CD_SCOPE; i < MAX_CD_SCOPE; i++) {
+ // If there is no argument there are no more scopes after it, break out.
+ if (argvars[i].v_type == VAR_UNKNOWN) {
+ break;
+ }
+ if (argvars[i].v_type != VAR_NUMBER) {
+ EMSG(_(e_invarg));
+ return;
+ }
+ scope_number[i] = argvars[i].vval.v_number;
+ // The scope is the current iteration step.
+ scope = i;
+ // It is an error for the scope number to be less than `-1`.
+ if (scope_number[i] < -1) {
+ EMSG(_(e_invarg));
+ return;
+ }
+ }
+
+ // Normalize scope, the number of the new scope will be 0.
+ if (scope_number[scope] < 0) {
+ // Arguments to `getcwd` always end at second-highest scope, so scope will
+ // always be <= `MAX_CD_SCOPE`.
+ scope++;
+ }
+
+ // Find the tabpage by number
+ if (scope_number[kCdScopeTab] == -1) {
+ tp = NULL;
+ } else if (scope_number[kCdScopeTab] > 0) {
+ tp = find_tabpage(scope_number[kCdScopeTab]);
+ if (!tp) {
+ EMSG(_("E5000: Cannot find tab number."));
+ return;
+ }
+ }
+
+ // Find the window in `tp` by number, `NULL` if none.
+ if (scope_number[kCdScopeWindow] == -1) {
+ win = NULL;
+ } else if (scope_number[kCdScopeWindow] >= 0) {
+ if (!tp) {
+ EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
+ return;
+ }
+
+ if (scope_number[kCdScopeWindow] > 0) {
+ win = find_win_by_nr(&argvars[0], curtab);
+ if (!win) {
+ EMSG(_("E5002: Cannot find window number."));
+ return;
+ }
+ }
+ }
+
cwd = xmalloc(MAXPATHL);
- if (os_dirname(cwd, MAXPATHL) != FAIL) {
- rettv->vval.v_string = vim_strsave(cwd);
+
+ switch (scope) {
+ case kCdScopeWindow:
+ assert(win);
+ from = win->w_localdir;
+ if (from) {
+ break;
+ }
+ case kCdScopeTab: // FALLTHROUGH
+ assert(tp);
+ from = tp->localdir;
+ if (from) {
+ break;
+ }
+ case kCdScopeGlobal: // FALLTHROUGH
+ // The `globaldir` variable is not always set.
+ if (globaldir) {
+ from = globaldir;
+ } else {
+ // We have to copy the OS path directly into output string.
+ if (os_dirname(cwd, MAXPATHL) == FAIL) {
+ EMSG(_("E41: Could not display path."));
+ goto end;
+ }
+ }
+ break;
+ }
+
+ if (from) {
+ xstrlcpy((char *)cwd, (char *)from, MAXPATHL);
+ }
+
+ rettv->vval.v_string = vim_strsave(cwd);
#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(rettv->vval.v_string);
+ slash_adjust(rettv->vval.v_string);
#endif
- }
+end:
xfree(cwd);
}
@@ -10412,6 +10207,14 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv)
dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id));
dict_add_nr_str(dict, "priority", (long)cur->priority, NULL);
dict_add_nr_str(dict, "id", (long)cur->id, NULL);
+
+ if (cur->conceal_char) {
+ char_u buf[MB_MAXBYTES + 1];
+
+ buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
+ dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf);
+ }
+
list_append_dict(rettv->vval.v_list, dict);
cur = cur->next;
}
@@ -10446,6 +10249,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos)
list_append_number(l,
(fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0);
if (getcurpos) {
+ update_curswant();
list_append_number(l, curwin->w_curswant == MAXCOL
? (varnumber_T)MAXCOL
: (varnumber_T)curwin->w_curswant + 1);
@@ -10518,9 +10322,10 @@ static void f_getreg(typval_T *argvars, typval_T *rettv)
rettv->v_type = VAR_LIST;
rettv->vval.v_list =
get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList);
- if (rettv->vval.v_list != NULL) {
- rettv->vval.v_list->lv_refcount++;
+ if (rettv->vval.v_list == NULL) {
+ rettv->vval.v_list = list_alloc();
}
+ rettv->vval.v_list->lv_refcount++;
} else {
rettv->v_type = VAR_STRING;
rettv->vval.v_string = get_reg_contents(regname, arg2 ? kGRegExprSrc : 0);
@@ -10534,8 +10339,6 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv)
{
char_u *strregname;
int regname;
- char_u buf[NUMBUFLEN + 2];
- long reglen = 0;
if (argvars[0].v_type != VAR_UNKNOWN) {
strregname = get_tv_string_chk(&argvars[0]);
@@ -10552,18 +10355,13 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv)
if (regname == 0)
regname = '"';
- buf[0] = NUL;
- buf[1] = NUL;
- switch (get_reg_type(regname, &reglen)) {
- case MLINE: buf[0] = 'V'; break;
- case MCHAR: buf[0] = 'v'; break;
- case MBLOCK:
- buf[0] = Ctrl_V;
- sprintf((char *)buf + 1, "%" PRId64, (int64_t)(reglen + 1));
- break;
- }
+ colnr_T reglen = 0;
+ char buf[NUMBUFLEN + 2];
+ MotionType reg_type = get_reg_type(regname, &reglen);
+ format_reg_type(reg_type, reglen, buf, ARRAY_SIZE(buf));
+
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave(buf);
+ rettv->vval.v_string = (char_u *)xstrdup(buf);
}
/*
@@ -10583,9 +10381,10 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv)
varname = get_tv_string_chk(&argvars[1]);
tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL));
if (tp != NULL && varname != NULL) {
- /* Set tp to be our tabpage, temporarily. Also set the window to the
- * first window in the tabpage, otherwise the window is not valid. */
- if (switch_win(&oldcurwin, &oldtabpage, tp->tp_firstwin, tp, TRUE) == OK) {
+ // Set tp to be our tabpage, temporarily. Also set the window to the
+ // first window in the tabpage, otherwise the window is not valid.
+ win_T *window = tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin;
+ if (switch_win(&oldcurwin, &oldtabpage, window, tp, true) == OK) {
// look up the variable
// Let gettabvar({nr}, "") return the "t:" dictionary.
v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', varname, FALSE);
@@ -10678,11 +10477,11 @@ getwinvar (
int off /* 1 for gettabwinvar() */
)
{
- win_T *win, *oldcurwin;
- char_u *varname;
- dictitem_T *v;
- tabpage_T *tp = NULL;
- tabpage_T *oldtabpage = NULL;
+ win_T *win, *oldcurwin;
+ char_u *varname;
+ dictitem_T *v;
+ tabpage_T *tp = NULL;
+ tabpage_T *oldtabpage = NULL;
bool done = false;
if (off == 1)
@@ -10697,12 +10496,16 @@ getwinvar (
rettv->vval.v_string = NULL;
if (win != NULL && varname != NULL) {
- /* Set curwin to be our win, temporarily. Also set the tabpage,
- * otherwise the window is not valid. */
- if (switch_win(&oldcurwin, &oldtabpage, win, tp, TRUE) == OK) {
- if (*varname == '&') { /* window-local-option */
- if (get_option_tv(&varname, rettv, 1) == OK)
+ // Set curwin to be our win, temporarily. Also set the tabpage,
+ // otherwise the window is not valid. Only do this when needed,
+ // autocommands get blocked.
+ bool need_switch_win = tp != curtab || win != curwin;
+ if (!need_switch_win
+ || switch_win(&oldcurwin, &oldtabpage, win, tp, true) == OK) {
+ if (*varname == '&') { // window-local-option
+ if (get_option_tv(&varname, rettv, 1) == OK) {
done = true;
+ }
} else {
// Look up the variable.
// Let getwinvar({nr}, "") return the "w:" dictionary.
@@ -10714,8 +10517,10 @@ getwinvar (
}
}
- /* restore previous notion of curwin */
- restore_win(oldcurwin, oldtabpage, TRUE);
+ if (need_switch_win) {
+ // restore previous notion of curwin
+ restore_win(oldcurwin, oldtabpage, true);
+ }
}
if (!done && argvars[off + 2].v_type != VAR_UNKNOWN)
@@ -10933,8 +10738,10 @@ static void f_has(typval_T *argvars, typval_T *rettv)
"tablineat",
"tag_binary",
"tag_old_static",
+ "termguicolors",
"termresponse",
"textobjects",
+ "timers",
"title",
"user-commands", /* was accidentally included in 5.4 */
"user_commands",
@@ -11014,12 +10821,103 @@ static void f_has_key(typval_T *argvars, typval_T *rettv)
get_tv_string(&argvars[1]), -1) != NULL;
}
-/*
- * "haslocaldir()" function
- */
+/// `haslocaldir([{win}[, {tab}]])` function
+///
+/// Returns `1` if the scope object has a local directory, `0` otherwise. If a
+/// scope object is not specified the current one is implied. This function
+/// share a lot of code with `f_getcwd`.
+///
+/// @pre The arguments must be of type number.
+/// @pre There may not be more than two arguments.
+/// @pre An argument may not be -1 if preceding arguments are not all -1.
+///
+/// @post The return value will be either the number `1` or `0`.
static void f_haslocaldir(typval_T *argvars, typval_T *rettv)
{
- rettv->vval.v_number = (curwin->w_localdir != NULL);
+ // Possible scope of working directory to return.
+ CdScope scope = MIN_CD_SCOPE;
+
+ // Numbers of the scope objects (window, tab) we want the working directory
+ // of. A `-1` means to skip this scope, a `0` means the current object.
+ int scope_number[] = {
+ [kCdScopeWindow] = 0, // Number of window to look at.
+ [kCdScopeTab ] = 0, // Number of tab to look at.
+ };
+
+ tabpage_T *tp = curtab; // The tabpage to look at.
+ win_T *win = curwin; // The window to look at.
+
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ // Pre-conditions and scope extraction together
+ for (int i = MIN_CD_SCOPE; i < MAX_CD_SCOPE; i++) {
+ if (argvars[i].v_type == VAR_UNKNOWN) {
+ break;
+ }
+ if (argvars[i].v_type != VAR_NUMBER) {
+ EMSG(_(e_invarg));
+ return;
+ }
+ scope_number[i] = argvars[i].vval.v_number;
+ // The scope is the current iteration step.
+ scope = i;
+ if (scope_number[i] < -1) {
+ EMSG(_(e_invarg));
+ return;
+ }
+ }
+
+ // Normalize scope, the number of the new scope will be 0.
+ if (scope_number[scope] < 0) {
+ // Arguments to `haslocaldir` always end at second-highest scope, so scope
+ // will always be <= `MAX_CD_SCOPE`.
+ scope++;
+ }
+
+ // Find the tabpage by number
+ if (scope_number[kCdScopeTab] == -1) {
+ tp = NULL;
+ } else if (scope_number[kCdScopeTab] > 0) {
+ tp = find_tabpage(scope_number[kCdScopeTab]);
+ if (!tp) {
+ EMSG(_("5000: Cannot find tab number."));
+ return;
+ }
+ }
+
+ // Find the window in `tp` by number, `NULL` if none.
+ if (scope_number[kCdScopeWindow] == -1) {
+ win = NULL;
+ } else if (scope_number[kCdScopeWindow] >= 0) {
+ if (!tp) {
+ EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
+ return;
+ }
+
+ if (scope_number[kCdScopeWindow] > 0) {
+ win = find_win_by_nr(&argvars[0], curtab);
+ if (!win) {
+ EMSG(_("E5002: Cannot find window number."));
+ return;
+ }
+ }
+ }
+
+ switch (scope) {
+ case kCdScopeWindow:
+ assert(win);
+ rettv->vval.v_number = win->w_localdir ? 1 : 0;
+ break;
+ case kCdScopeTab:
+ assert(tp);
+ rettv->vval.v_number = tp->localdir ? 1 : 0;
+ break;
+ case kCdScopeGlobal:
+ // The global scope never has a local directory
+ rettv->vval.v_number = 0;
+ break;
+ }
}
/*
@@ -11052,21 +10950,22 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv)
*/
static void f_histadd(typval_T *argvars, typval_T *rettv)
{
- int histype;
+ HistoryType histype;
char_u *str;
char_u buf[NUMBUFLEN];
- rettv->vval.v_number = FALSE;
- if (check_restricted() || check_secure())
+ rettv->vval.v_number = false;
+ if (check_restricted() || check_secure()) {
return;
- str = get_tv_string_chk(&argvars[0]); /* NULL on type error */
- histype = str != NULL ? get_histtype(str) : -1;
- if (histype >= 0) {
+ }
+ str = get_tv_string_chk(&argvars[0]); // NULL on type error
+ histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID;
+ if (histype != HIST_INVALID) {
str = get_tv_string_buf(&argvars[1], buf);
if (*str != NUL) {
init_history();
- add_to_history(histype, str, FALSE, NUL);
- rettv->vval.v_number = TRUE;
+ add_to_history(histype, str, false, NUL);
+ rettv->vval.v_number = true;
return;
}
}
@@ -11081,20 +10980,21 @@ static void f_histdel(typval_T *argvars, typval_T *rettv)
char_u buf[NUMBUFLEN];
char_u *str;
- str = get_tv_string_chk(&argvars[0]); /* NULL on type error */
- if (str == NULL)
+ str = get_tv_string_chk(&argvars[0]); // NULL on type error
+ if (str == NULL) {
n = 0;
- else if (argvars[1].v_type == VAR_UNKNOWN)
- /* only one argument: clear entire history */
- n = clr_history(get_histtype(str));
- else if (argvars[1].v_type == VAR_NUMBER)
- /* index given: remove that entry */
- n = del_history_idx(get_histtype(str),
- (int)get_tv_number(&argvars[1]));
- else
- /* string given: remove all matching entries */
- n = del_history_entry(get_histtype(str),
- get_tv_string_buf(&argvars[1], buf));
+ } else if (argvars[1].v_type == VAR_UNKNOWN) {
+ // only one argument: clear entire history
+ n = clr_history(get_histtype(str, STRLEN(str), false));
+ } else if (argvars[1].v_type == VAR_NUMBER) {
+ // index given: remove that entry
+ n = del_history_idx(get_histtype(str, STRLEN(str), false),
+ (int) get_tv_number(&argvars[1]));
+ } else {
+ // string given: remove all matching entries
+ n = del_history_entry(get_histtype(str, STRLEN(str), false),
+ get_tv_string_buf(&argvars[1], buf));
+ }
rettv->vval.v_number = n;
}
@@ -11103,20 +11003,21 @@ static void f_histdel(typval_T *argvars, typval_T *rettv)
*/
static void f_histget(typval_T *argvars, typval_T *rettv)
{
- int type;
+ HistoryType type;
int idx;
char_u *str;
- str = get_tv_string_chk(&argvars[0]); /* NULL on type error */
- if (str == NULL)
+ str = get_tv_string_chk(&argvars[0]); // NULL on type error
+ if (str == NULL) {
rettv->vval.v_string = NULL;
- else {
- type = get_histtype(str);
- if (argvars[1].v_type == VAR_UNKNOWN)
+ } else {
+ type = get_histtype(str, STRLEN(str), false);
+ if (argvars[1].v_type == VAR_UNKNOWN) {
idx = get_history_idx(type);
- else
+ } else {
idx = (int)get_tv_number_chk(&argvars[1], NULL);
- /* -1 on type error */
+ }
+ // -1 on type error
rettv->vval.v_string = vim_strsave(get_history_entry(type, idx));
}
rettv->v_type = VAR_STRING;
@@ -11131,11 +11032,13 @@ static void f_histnr(typval_T *argvars, typval_T *rettv)
char_u *history = get_tv_string_chk(&argvars[0]);
- i = history == NULL ? HIST_CMD - 1 : get_histtype(history);
- if (i >= HIST_CMD && i < HIST_COUNT)
+ i = history == NULL ? HIST_CMD - 1 : get_histtype(history, STRLEN(history),
+ false);
+ if (i != HIST_INVALID) {
i = get_history_idx(i);
- else
+ } else {
i = -1;
+ }
rettv->vval.v_number = i;
}
@@ -11442,14 +11345,18 @@ static void f_insert(typval_T *argvars, typval_T *rettv)
list_T *l;
int error = FALSE;
- if (argvars[0].v_type != VAR_LIST)
+ if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "insert()");
- else if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock, (char_u *)_("insert() argument"))) {
- if (argvars[2].v_type != VAR_UNKNOWN)
+ } else if ((l = argvars[0].vval.v_list) != NULL
+ && !tv_check_lock(l->lv_lock,
+ (char_u *)N_("insert() argument"), true)) {
+ if (argvars[2].v_type != VAR_UNKNOWN) {
before = get_tv_number_chk(&argvars[2], &error);
- if (error)
- return; /* type error; errmsg already given */
+ }
+ if (error) {
+ // type error; errmsg already given
+ return;
+ }
if (before == l->lv_len)
item = NULL;
@@ -11767,7 +11674,7 @@ static char **tv_to_argv(typval_T *cmd_tv, char **cmd)
assert(argl->lv_first);
const char_u *exe = get_tv_string_chk(&argl->lv_first->li_tv);
- if (!exe || !os_can_exe(exe, NULL)) {
+ if (!exe || !os_can_exe(exe, NULL, true)) {
// String is not executable
if (exe) {
EMSG2(e_jobexe, exe);
@@ -12023,6 +11930,47 @@ static void f_join(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string = NULL;
}
+/// json_decode() function
+static void f_json_decode(typval_T *argvars, typval_T *rettv)
+{
+ char numbuf[NUMBUFLEN];
+ char *s = NULL;
+ char *tofree = NULL;
+ size_t len;
+ if (argvars[0].v_type == VAR_LIST) {
+ if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &s)) {
+ EMSG(_("E474: Failed to convert list to string"));
+ return;
+ }
+ tofree = s;
+ if (s == NULL) {
+ assert(len == 0);
+ s = "";
+ }
+ } else {
+ s = (char *) get_tv_string_buf_chk(&argvars[0], (char_u *) numbuf);
+ if (s) {
+ len = strlen(s);
+ } else {
+ return;
+ }
+ }
+ if (json_decode_string(s, len, rettv) == FAIL) {
+ emsgf(_("E474: Failed to parse %.*s"), (int) len, s);
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+ }
+ assert(rettv->v_type != VAR_UNKNOWN);
+ xfree(tofree);
+}
+
+/// json_encode() function
+static void f_json_encode(typval_T *argvars, typval_T *rettv)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = (char_u *) encode_tv2json(&argvars[0], NULL);
+}
+
/*
* "keys()" function
*/
@@ -12064,7 +12012,10 @@ static void f_len(typval_T *argvars, typval_T *rettv)
case VAR_DICT:
rettv->vval.v_number = dict_len(argvars[0].vval.v_dict);
break;
- default:
+ case VAR_UNKNOWN:
+ case VAR_SPECIAL:
+ case VAR_FLOAT:
+ case VAR_FUNC:
EMSG(_("E701: Invalid type for len()"));
break;
}
@@ -12224,8 +12175,9 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
mode = get_map_mode(&which, 0);
- keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, FALSE);
- rhs = check_map(keys, mode, exact, FALSE, abbr, &mp, &buffer_local);
+ keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false,
+ CPO_TO_CPO_FLAGS);
+ rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local);
xfree(keys_buf);
if (!get_dict) {
@@ -12387,9 +12339,10 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
break;
}
xfree(tofree);
- tofree = str = (char_u *) echo_string(&li->li_tv, NULL);
- if (str == NULL)
+ tofree = str = (char_u *) encode_tv2echo(&li->li_tv, NULL);
+ if (str == NULL) {
break;
+ }
}
match = vim_regexec_nl(&regmatch, str, (colnr_T)startcol);
@@ -12472,7 +12425,8 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv)
char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */
int prio = 10; /* default priority */
int id = -1;
- int error = FALSE;
+ int error = false;
+ char_u *conceal_char = NULL;
rettv->vval.v_number = -1;
@@ -12480,17 +12434,31 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv)
return;
if (argvars[2].v_type != VAR_UNKNOWN) {
prio = get_tv_number_chk(&argvars[2], &error);
- if (argvars[3].v_type != VAR_UNKNOWN)
+ if (argvars[3].v_type != VAR_UNKNOWN) {
id = get_tv_number_chk(&argvars[3], &error);
+ if (argvars[4].v_type != VAR_UNKNOWN) {
+ if (argvars[4].v_type != VAR_DICT) {
+ EMSG(_(e_dictreq));
+ return;
+ }
+ if (dict_find(argvars[4].vval.v_dict,
+ (char_u *)"conceal", -1) != NULL) {
+ conceal_char = get_dict_string(argvars[4].vval.v_dict,
+ (char_u *)"conceal", false);
+ }
+ }
+ }
}
- if (error == TRUE)
+ if (error == true) {
return;
+ }
if (id >= 1 && id <= 3) {
EMSGN("E798: ID is reserved for \":match\": %" PRId64, id);
return;
}
- rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL);
+ rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL,
+ conceal_char);
}
static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ALL
@@ -12518,12 +12486,24 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_
int error = false;
int prio = 10;
int id = -1;
+ char_u *conceal_char = NULL;
if (argvars[2].v_type != VAR_UNKNOWN) {
- prio = get_tv_number_chk(&argvars[2], &error);
- if (argvars[3].v_type != VAR_UNKNOWN) {
- id = get_tv_number_chk(&argvars[3], &error);
+ prio = get_tv_number_chk(&argvars[2], &error);
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ id = get_tv_number_chk(&argvars[3], &error);
+ if (argvars[4].v_type != VAR_UNKNOWN) {
+ if (argvars[4].v_type != VAR_DICT) {
+ EMSG(_(e_dictreq));
+ return;
+ }
+ if (dict_find(argvars[4].vval.v_dict,
+ (char_u *)"conceal", -1) != NULL) {
+ conceal_char = get_dict_string(argvars[4].vval.v_dict,
+ (char_u *)"conceal", false);
+ }
}
+ }
}
if (error == true) {
return;
@@ -12535,7 +12515,8 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_
return;
}
- rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l);
+ rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l,
+ conceal_char);
}
/*
@@ -12759,289 +12740,6 @@ static void f_mode(typval_T *argvars, typval_T *rettv)
rettv->v_type = VAR_STRING;
}
-/// Msgpack callback for writing to readfile()-style list
-static int msgpack_list_write(void *data, const char *buf, size_t len)
-{
- if (len == 0) {
- return 0;
- }
- list_T *const list = (list_T *) data;
- const char *const end = buf + len;
- const char *line_end = buf;
- if (list->lv_last == NULL) {
- list_append_string(list, NULL, 0);
- }
- listitem_T *li = list->lv_last;
- do {
- const char *line_start = line_end;
- line_end = xmemscan(line_start, NL, end - line_start);
- if (line_end == line_start) {
- list_append_allocated_string(list, NULL);
- } else {
- const size_t line_length = line_end - line_start;
- char *str;
- if (li == NULL) {
- str = xmemdupz(line_start, line_length);
- } else {
- const size_t li_len = (li->li_tv.vval.v_string == NULL
- ? 0
- : STRLEN(li->li_tv.vval.v_string));
- li->li_tv.vval.v_string = xrealloc(li->li_tv.vval.v_string,
- li_len + line_length + 1);
- str = (char *) li->li_tv.vval.v_string + li_len;
- memmove(str, line_start, line_length);
- str[line_length] = 0;
- }
- for (size_t i = 0; i < line_length; i++) {
- if (str[i] == NUL) {
- str[i] = NL;
- }
- }
- if (li == NULL) {
- list_append_allocated_string(list, str);
- } else {
- li = NULL;
- }
- if (line_end == end - 1) {
- list_append_allocated_string(list, NULL);
- }
- }
- line_end++;
- } while (line_end < end);
- return 0;
-}
-
-/// Convert readfile()-style list to a char * buffer with length
-///
-/// @param[in] list Converted list.
-/// @param[out] ret_len Resulting buffer length.
-/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is
-/// zero.
-///
-/// @return true in case of success, false in case of failure.
-static inline bool vim_list_to_buf(const list_T *const list,
- size_t *const ret_len, char **const ret_buf)
- FUNC_ATTR_NONNULL_ARG(2,3) FUNC_ATTR_WARN_UNUSED_RESULT
-{
- size_t len = 0;
- if (list != NULL) {
- for (const listitem_T *li = list->lv_first;
- li != NULL;
- li = li->li_next) {
- if (li->li_tv.v_type != VAR_STRING) {
- return false;
- }
- len++;
- if (li->li_tv.vval.v_string != 0) {
- len += STRLEN(li->li_tv.vval.v_string);
- }
- }
- if (len) {
- len--;
- }
- }
- *ret_len = len;
- if (len == 0) {
- *ret_buf = NULL;
- return true;
- }
- ListReaderState lrstate = init_lrstate(list);
- char *const buf = xmalloc(len);
- size_t read_bytes;
- if (read_from_list(&lrstate, buf, len, &read_bytes) != OK) {
- assert(false);
- }
- assert(len == read_bytes);
- *ret_buf = buf;
- return true;
-}
-
-/// Show a error message when converting to msgpack value
-///
-/// @param[in] msg Error message to dump. Must contain exactly two %s that
-/// will be replaced with what was being dumped: first with
-/// something like “F†or “function argumentâ€, second with path
-/// to the failed value.
-/// @param[in] mpstack Path to the failed value.
-/// @param[in] objname Dumped object name.
-///
-/// @return FAIL.
-static int conv_error(const char *const msg, const MPConvStack *const mpstack,
- const char *const objname)
- FUNC_ATTR_NONNULL_ALL
-{
- garray_T msg_ga;
- ga_init(&msg_ga, (int)sizeof(char), 80);
- char *const key_msg = _("key %s");
- char *const key_pair_msg = _("key %s at index %i from special map");
- char *const idx_msg = _("index %i");
- for (size_t i = 0; i < kv_size(*mpstack); i++) {
- if (i != 0) {
- ga_concat(&msg_ga, (char_u *) ", ");
- }
- MPConvStackVal v = kv_A(*mpstack, i);
- switch (v.type) {
- case kMPConvDict: {
- typval_T key_tv = {
- .v_type = VAR_STRING,
- .vval = { .v_string = (v.data.d.hi == NULL
- ? v.data.d.dict->dv_hashtab.ht_array
- : (v.data.d.hi - 1))->hi_key },
- };
- char *const key = tv2string(&key_tv, NULL);
- vim_snprintf((char *) IObuff, IOSIZE, key_msg, key);
- xfree(key);
- ga_concat(&msg_ga, IObuff);
- break;
- }
- case kMPConvPairs:
- case kMPConvList: {
- int idx = 0;
- const listitem_T *li;
- for (li = v.data.l.list->lv_first;
- li != NULL && li->li_next != v.data.l.li;
- li = li->li_next) {
- idx++;
- }
- if (v.type == kMPConvList
- || li == NULL
- || (li->li_tv.v_type != VAR_LIST
- && li->li_tv.vval.v_list->lv_len <= 0)) {
- vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx);
- ga_concat(&msg_ga, IObuff);
- } else {
- typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv;
- char *const key = echo_string(&key_tv, NULL);
- vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx);
- xfree(key);
- ga_concat(&msg_ga, IObuff);
- }
- break;
- }
- }
- }
- EMSG3(msg, objname, (kv_size(*mpstack) == 0
- ? _("itself")
- : (char *) msg_ga.ga_data));
- ga_clear(&msg_ga);
- return FAIL;
-}
-
-#define CONV_STRING(buf, len) \
- do { \
- if (buf == NULL) { \
- msgpack_pack_bin(packer, 0); \
- } else { \
- const size_t len_ = (len); \
- msgpack_pack_bin(packer, len_); \
- msgpack_pack_bin_body(packer, buf, len_); \
- } \
- } while (0)
-
-#define CONV_STR_STRING(buf, len) \
- do { \
- if (buf == NULL) { \
- msgpack_pack_str(packer, 0); \
- } else { \
- const size_t len_ = (len); \
- msgpack_pack_str(packer, len_); \
- msgpack_pack_str_body(packer, buf, len_); \
- } \
- } while (0)
-
-#define CONV_EXT_STRING(buf, len, type) \
- do { \
- if (buf == NULL) { \
- msgpack_pack_ext(packer, 0, type); \
- } else { \
- const size_t len_ = (len); \
- msgpack_pack_ext(packer, len_, (int8_t) type); \
- msgpack_pack_ext_body(packer, buf, len_); \
- } \
- } while (0)
-
-#define CONV_NUMBER(num) \
- msgpack_pack_int64(packer, (int64_t) (num))
-
-#define CONV_FLOAT(flt) \
- msgpack_pack_double(packer, (double) (flt))
-
-#define CONV_FUNC(fun) \
- return conv_error(_("E951: Error while dumping %s, %s: " \
- "attempt to dump function reference"), \
- mpstack, objname)
-
-#define CONV_EMPTY_LIST() \
- msgpack_pack_array(packer, 0)
-
-#define CONV_LIST_START(lst) \
- msgpack_pack_array(packer, (lst)->lv_len)
-
-#define CONV_EMPTY_DICT() \
- msgpack_pack_map(packer, 0)
-
-#define CONV_SPECIAL_NIL() \
- msgpack_pack_nil(packer)
-
-#define CONV_SPECIAL_BOOL(num) \
- do { \
- if ((num)) { \
- msgpack_pack_true(packer); \
- } else { \
- msgpack_pack_false(packer); \
- } \
- } while (0)
-
-#define CONV_UNSIGNED_NUMBER(num) \
- msgpack_pack_uint64(packer, (num))
-
-#define CONV_SPECIAL_MAP_START(lst) \
- msgpack_pack_map(packer, (lst)->lv_len)
-
-#define CONV_DICT_START(dct) \
- msgpack_pack_map(packer, (dct)->dv_hashtab.ht_used)
-
-#define CONV_DICT_END(dct)
-
-#define CONV_DICT_AFTER_KEY(dct)
-
-#define CONV_DICT_BETWEEN_ITEMS(dct)
-
-#define CONV_LIST_END(lst)
-
-#define CONV_LIST_BETWEEN_ITEMS(lst)
-
-#define CONV_RECURSE(val, conv_type) \
- return conv_error(_("E952: Unable to dump %s: " \
- "container references itself in %s"), \
- mpstack, objname)
-
-#define CONV_ALLOW_SPECIAL true
-
-DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
-
-#undef CONV_STRING
-#undef CONV_STR_STRING
-#undef CONV_EXT_STRING
-#undef CONV_NUMBER
-#undef CONV_FLOAT
-#undef CONV_FUNC
-#undef CONV_EMPTY_LIST
-#undef CONV_LIST_START
-#undef CONV_EMPTY_DICT
-#undef CONV_SPECIAL_NIL
-#undef CONV_SPECIAL_BOOL
-#undef CONV_UNSIGNED_NUMBER
-#undef CONV_SPECIAL_MAP_START
-#undef CONV_DICT_START
-#undef CONV_DICT_END
-#undef CONV_DICT_AFTER_KEY
-#undef CONV_DICT_BETWEEN_ITEMS
-#undef CONV_LIST_END
-#undef CONV_LIST_BETWEEN_ITEMS
-#undef CONV_RECURSE
-#undef CONV_ALLOW_SPECIAL
-
/// "msgpackdump()" function
static void f_msgpackdump(typval_T *argvars, typval_T *rettv)
FUNC_ATTR_NONNULL_ALL
@@ -13055,7 +12753,7 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv)
if (list == NULL) {
return;
}
- msgpack_packer *lpacker = msgpack_packer_new(ret_list, &msgpack_list_write);
+ msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write);
const char *const msg = _("msgpackdump() argument, index %i");
// Assume that translation will not take more then 4 times more space
char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN];
@@ -13063,307 +12761,13 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv)
for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
vim_snprintf(msgbuf, sizeof(msgbuf), (char *) msg, idx);
idx++;
- if (vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) {
+ if (encode_vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) {
break;
}
}
msgpack_packer_free(lpacker);
}
-/// Read bytes from list
-///
-/// @param[in,out] state Structure describing position in list from which
-/// reading should start. Is updated to reflect position
-/// at which reading ended.
-/// @param[out] buf Buffer to write to.
-/// @param[in] nbuf Buffer length.
-/// @param[out] read_bytes Is set to amount of bytes read.
-///
-/// @return OK when reading was finished, FAIL in case of error (i.e. list item
-/// was not a string), NOTDONE if reading was successfull, but there are
-/// more bytes to read.
-static int read_from_list(ListReaderState *const state, char *const buf,
- const size_t nbuf, size_t *const read_bytes)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
- char *const buf_end = buf + nbuf;
- char *p = buf;
- while (p < buf_end) {
- for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) {
- const char ch = state->li->li_tv.vval.v_string[state->offset++];
- *p++ = (ch == NL ? NUL : ch);
- }
- if (p < buf_end) {
- state->li = state->li->li_next;
- if (state->li == NULL) {
- *read_bytes = (size_t) (p - buf);
- return OK;
- }
- *p++ = NL;
- if (state->li->li_tv.v_type != VAR_STRING) {
- *read_bytes = (size_t) (p - buf);
- return FAIL;
- }
- state->offset = 0;
- state->li_length = (state->li->li_tv.vval.v_string == NULL
- ? 0
- : STRLEN(state->li->li_tv.vval.v_string));
- }
- }
- *read_bytes = nbuf;
- return (state->offset < state->li_length || state->li->li_next != NULL
- ? NOTDONE
- : OK);
-}
-
-/// Initialize ListReaderState structure
-static inline ListReaderState init_lrstate(const list_T *const list)
- FUNC_ATTR_NONNULL_ALL
-{
- return (ListReaderState) {
- .li = list->lv_first,
- .offset = 0,
- .li_length = (list->lv_first->li_tv.vval.v_string == NULL
- ? 0
- : STRLEN(list->lv_first->li_tv.vval.v_string)),
- };
-}
-
-/// Convert msgpack object to a VimL one
-int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
-#define INIT_SPECIAL_DICT(tv, type, val) \
- do { \
- dict_T *const dict = dict_alloc(); \
- dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); \
- type_di->di_tv.v_type = VAR_LIST; \
- type_di->di_tv.v_lock = 0; \
- type_di->di_tv.vval.v_list = (list_T *) msgpack_type_lists[type]; \
- type_di->di_tv.vval.v_list->lv_refcount++; \
- dict_add(dict, type_di); \
- dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); \
- val_di->di_tv = val; \
- dict_add(dict, val_di); \
- tv->v_type = VAR_DICT; \
- dict->dv_refcount++; \
- tv->vval.v_dict = dict; \
- } while (0)
- switch (mobj.type) {
- case MSGPACK_OBJECT_NIL: {
- INIT_SPECIAL_DICT(rettv, kMPNil, ((typval_T) {
- .v_type = VAR_NUMBER,
- .v_lock = 0,
- .vval = { .v_number = 0 },
- }));
- break;
- }
- case MSGPACK_OBJECT_BOOLEAN: {
- INIT_SPECIAL_DICT(rettv, kMPBoolean,
- ((typval_T) {
- .v_type = VAR_NUMBER,
- .v_lock = 0,
- .vval = {
- .v_number = (varnumber_T) mobj.via.boolean,
- },
- }));
- break;
- }
- case MSGPACK_OBJECT_POSITIVE_INTEGER: {
- if (mobj.via.u64 <= VARNUMBER_MAX) {
- *rettv = (typval_T) {
- .v_type = VAR_NUMBER,
- .v_lock = 0,
- .vval = { .v_number = (varnumber_T) mobj.via.u64 },
- };
- } else {
- list_T *const list = list_alloc();
- list->lv_refcount++;
- INIT_SPECIAL_DICT(rettv, kMPInteger,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- uint64_t n = mobj.via.u64;
- list_append_number(list, 1);
- list_append_number(list, (varnumber_T) ((n >> 62) & 0x3));
- list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF));
- list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF));
- }
- break;
- }
- case MSGPACK_OBJECT_NEGATIVE_INTEGER: {
- if (mobj.via.i64 >= VARNUMBER_MIN) {
- *rettv = (typval_T) {
- .v_type = VAR_NUMBER,
- .v_lock = 0,
- .vval = { .v_number = (varnumber_T) mobj.via.i64 },
- };
- } else {
- list_T *const list = list_alloc();
- list->lv_refcount++;
- INIT_SPECIAL_DICT(rettv, kMPInteger,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- uint64_t n = -((uint64_t) mobj.via.i64);
- list_append_number(list, -1);
- list_append_number(list, (varnumber_T) ((n >> 62) & 0x3));
- list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF));
- list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF));
- }
- break;
- }
- case MSGPACK_OBJECT_FLOAT: {
- *rettv = (typval_T) {
- .v_type = VAR_FLOAT,
- .v_lock = 0,
- .vval = { .v_float = mobj.via.f64 },
- };
- break;
- }
- case MSGPACK_OBJECT_STR: {
- list_T *const list = list_alloc();
- list->lv_refcount++;
- INIT_SPECIAL_DICT(rettv, kMPString,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- if (msgpack_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size)
- == -1) {
- return FAIL;
- }
- break;
- }
- case MSGPACK_OBJECT_BIN: {
- if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) {
- *rettv = (typval_T) {
- .v_type = VAR_STRING,
- .v_lock = 0,
- .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) },
- };
- break;
- }
- list_T *const list = list_alloc();
- list->lv_refcount++;
- INIT_SPECIAL_DICT(rettv, kMPBinary,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- if (msgpack_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size)
- == -1) {
- return FAIL;
- }
- break;
- }
- case MSGPACK_OBJECT_ARRAY: {
- list_T *const list = list_alloc();
- list->lv_refcount++;
- *rettv = (typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- };
- for (size_t i = 0; i < mobj.via.array.size; i++) {
- listitem_T *const li = listitem_alloc();
- li->li_tv.v_type = VAR_UNKNOWN;
- list_append(list, li);
- if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) {
- return FAIL;
- }
- }
- break;
- }
- case MSGPACK_OBJECT_MAP: {
- for (size_t i = 0; i < mobj.via.map.size; i++) {
- if (mobj.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR
- || mobj.via.map.ptr[i].key.via.str.size == 0
- || memchr(mobj.via.map.ptr[i].key.via.str.ptr, NUL,
- mobj.via.map.ptr[i].key.via.str.size) != NULL) {
- goto msgpack_to_vim_generic_map;
- }
- }
- dict_T *const dict = dict_alloc();
- dict->dv_refcount++;
- *rettv = (typval_T) {
- .v_type = VAR_DICT,
- .v_lock = 0,
- .vval = { .v_dict = dict },
- };
- for (size_t i = 0; i < mobj.via.map.size; i++) {
- dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key)
- + mobj.via.map.ptr[i].key.via.str.size);
- memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr,
- mobj.via.map.ptr[i].key.via.str.size);
- di->di_tv.v_type = VAR_UNKNOWN;
- if (dict_add(dict, di) == FAIL) {
- // Duplicate key: fallback to generic map
- clear_tv(rettv);
- xfree(di);
- goto msgpack_to_vim_generic_map;
- }
- if (msgpack_to_vim(mobj.via.map.ptr[i].val, &di->di_tv) == FAIL) {
- return FAIL;
- }
- }
- break;
-msgpack_to_vim_generic_map: {}
- list_T *const list = list_alloc();
- list->lv_refcount++;
- INIT_SPECIAL_DICT(rettv, kMPMap,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- for (size_t i = 0; i < mobj.via.map.size; i++) {
- list_T *const kv_pair = list_alloc();
- list_append_list(list, kv_pair);
- listitem_T *const key_li = listitem_alloc();
- key_li->li_tv.v_type = VAR_UNKNOWN;
- list_append(kv_pair, key_li);
- listitem_T *const val_li = listitem_alloc();
- val_li->li_tv.v_type = VAR_UNKNOWN;
- list_append(kv_pair, val_li);
- if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) {
- return FAIL;
- }
- if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) {
- return FAIL;
- }
- }
- break;
- }
- case MSGPACK_OBJECT_EXT: {
- list_T *const list = list_alloc();
- list->lv_refcount++;
- list_append_number(list, mobj.via.ext.type);
- list_T *const ext_val_list = list_alloc();
- list_append_list(list, ext_val_list);
- INIT_SPECIAL_DICT(rettv, kMPExt,
- ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = 0,
- .vval = { .v_list = list },
- }));
- if (msgpack_list_write((void *) ext_val_list, mobj.via.ext.ptr,
- mobj.via.ext.size) == -1) {
- return FAIL;
- }
- break;
- }
- }
-#undef INIT_SPECIAL_DICT
- return OK;
-}
-
/// "msgpackparse" function
static void f_msgpackparse(typval_T *argvars, typval_T *rettv)
FUNC_ATTR_NONNULL_ALL
@@ -13381,7 +12785,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv)
EMSG2(_(e_invarg2), "List item is not a string");
return;
}
- ListReaderState lrstate = init_lrstate(list);
+ ListReaderState lrstate = encode_init_lrstate(list);
msgpack_unpacker *const unpacker = msgpack_unpacker_new(IOSIZE);
if (unpacker == NULL) {
EMSG(_(e_outofmem));
@@ -13395,7 +12799,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv)
goto f_msgpackparse_exit;
}
size_t read_bytes;
- const int rlret = read_from_list(
+ const int rlret = encode_read_from_list(
&lrstate, msgpack_unpacker_buffer(unpacker), IOSIZE, &read_bytes);
if (rlret == FAIL) {
EMSG2(_(e_invarg2), "List item is not a string");
@@ -13903,20 +13307,20 @@ static void f_remove(typval_T *argvars, typval_T *rettv)
char_u *key;
dict_T *d;
dictitem_T *di;
- char *arg_errmsg = N_("remove() argument");
+ char_u *arg_errmsg = (char_u *)N_("remove() argument");
if (argvars[0].v_type == VAR_DICT) {
- if (argvars[2].v_type != VAR_UNKNOWN)
+ if (argvars[2].v_type != VAR_UNKNOWN) {
EMSG2(_(e_toomanyarg), "remove()");
- else if ((d = argvars[0].vval.v_dict) != NULL
- && !tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg))) {
+ } else if ((d = argvars[0].vval.v_dict) != NULL
+ && !tv_check_lock(d->dv_lock, arg_errmsg, true)) {
key = get_tv_string_chk(&argvars[1]);
if (key != NULL) {
di = dict_find(d, key, -1);
if (di == NULL) {
EMSG2(_(e_dictkey), key);
- } else if (!var_check_fixed(di->di_flags, (char_u *)_(arg_errmsg))
- && !var_check_ro(di->di_flags, (char_u *)_(arg_errmsg))) {
+ } else if (!var_check_fixed(di->di_flags, arg_errmsg, true)
+ && !var_check_ro(di->di_flags, arg_errmsg, true)) {
*rettv = di->di_tv;
init_tv(&di->di_tv);
dictitem_remove(d, di);
@@ -13926,11 +13330,11 @@ static void f_remove(typval_T *argvars, typval_T *rettv)
}
}
}
- } else if (argvars[0].v_type != VAR_LIST)
+ } else if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listdictarg), "remove()");
- else if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg))) {
- int error = FALSE;
+ } else if ((l = argvars[0].vval.v_list) != NULL
+ && !tv_check_lock(l->lv_lock, arg_errmsg, true)) {
+ int error = (int)false;
idx = get_tv_number_chk(&argvars[1], &error);
if (error)
@@ -14204,10 +13608,11 @@ static void f_reverse(typval_T *argvars, typval_T *rettv)
list_T *l;
listitem_T *li, *ni;
- if (argvars[0].v_type != VAR_LIST)
+ if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "reverse()");
- else if ((l = argvars[0].vval.v_list) != NULL
- && !tv_check_lock(l->lv_lock, (char_u *)_("reverse() argument"))) {
+ } else if ((l = argvars[0].vval.v_list) != NULL
+ && !tv_check_lock(l->lv_lock,
+ (char_u *)N_("reverse() argument"), true)) {
li = l->lv_last;
l->lv_first = l->lv_last = NULL;
l->lv_len = 0;
@@ -14223,14 +13628,14 @@ static void f_reverse(typval_T *argvars, typval_T *rettv)
}
}
-#define SP_NOMOVE 0x01 /* don't move cursor */
-#define SP_REPEAT 0x02 /* repeat to find outer pair */
-#define SP_RETCOUNT 0x04 /* return matchcount */
-#define SP_SETPCMARK 0x08 /* set previous context mark */
-#define SP_START 0x10 /* accept match at start position */
-#define SP_SUBPAT 0x20 /* return nr of matching sub-pattern */
-#define SP_END 0x40 /* leave cursor at end of match */
-
+#define SP_NOMOVE 0x01 ///< don't move cursor
+#define SP_REPEAT 0x02 ///< repeat to find outer pair
+#define SP_RETCOUNT 0x04 ///< return matchcount
+#define SP_SETPCMARK 0x08 ///< set previous context mark
+#define SP_START 0x10 ///< accept match at start position
+#define SP_SUBPAT 0x20 ///< return nr of matching sub-pattern
+#define SP_END 0x40 ///< leave cursor at end of match
+#define SP_COLUMN 0x80 ///< start at cursor column
/*
* Get flags for a search function.
@@ -14256,13 +13661,14 @@ static int get_search_arg(typval_T *varp, int *flagsp)
default: mask = 0;
if (flagsp != NULL)
switch (*flags) {
- case 'c': mask = SP_START; break;
- case 'e': mask = SP_END; break;
- case 'm': mask = SP_RETCOUNT; break;
- case 'n': mask = SP_NOMOVE; break;
- case 'p': mask = SP_SUBPAT; break;
- case 'r': mask = SP_REPEAT; break;
- case 's': mask = SP_SETPCMARK; break;
+ case 'c': mask = SP_START; break;
+ case 'e': mask = SP_END; break;
+ case 'm': mask = SP_RETCOUNT; break;
+ case 'n': mask = SP_NOMOVE; break;
+ case 'p': mask = SP_SUBPAT; break;
+ case 'r': mask = SP_REPEAT; break;
+ case 's': mask = SP_SETPCMARK; break;
+ case 'z': mask = SP_COLUMN; break;
}
if (mask == 0) {
EMSG2(_(e_invarg2), flags);
@@ -14278,9 +13684,7 @@ static int get_search_arg(typval_T *varp, int *flagsp)
return dir;
}
-/*
- * Shared by search() and searchpos() functions
- */
+// Shared by search() and searchpos() functions.
static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
{
int flags;
@@ -14301,10 +13705,15 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
if (dir == 0)
goto theend;
flags = *flagsp;
- if (flags & SP_START)
+ if (flags & SP_START) {
options |= SEARCH_START;
- if (flags & SP_END)
+ }
+ if (flags & SP_END) {
options |= SEARCH_END;
+ }
+ if (flags & SP_COLUMN) {
+ options |= SEARCH_COL;
+ }
/* Optional arguments: line number to stop searching and timeout. */
if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
@@ -15092,6 +14501,38 @@ static void f_setcmdpos(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = set_cmdline_pos(pos);
}
+
+/// "setfperm({fname}, {mode})" function
+static void f_setfperm(typval_T *argvars, typval_T *rettv)
+{
+ rettv->vval.v_number = 0;
+
+ char_u *fname = get_tv_string_chk(&argvars[0]);
+ if (fname == NULL) {
+ return;
+ }
+
+ char_u modebuf[NUMBUFLEN];
+ char_u *mode_str = get_tv_string_buf_chk(&argvars[1], modebuf);
+ if (mode_str == NULL) {
+ return;
+ }
+ if (STRLEN(mode_str) != 9) {
+ EMSG2(_(e_invarg2), mode_str);
+ return;
+ }
+
+ int mask = 1;
+ int mode = 0;
+ for (int i = 8; i >= 0; i--) {
+ if (mode_str[i] != '-') {
+ mode |= mask;
+ }
+ mask = mask << 1;
+ }
+ rettv->vval.v_number = os_setperm(fname, mode) == OK;
+}
+
/*
* "setline()" function
*/
@@ -15156,33 +14597,64 @@ static void f_setline(typval_T *argvars, typval_T *rettv)
appended_lines_mark(lcount, added);
}
-
-/*
- * Used by "setqflist()" and "setloclist()" functions
- */
-static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *rettv)
+/// Create quickfix/location list from VimL values
+///
+/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid
+/// list_arg, action_arg and title_arg arguments in which case errors out,
+/// including VAR_UNKNOWN parameters.
+///
+/// @param[in,out] wp Window to create location list for. May be NULL in
+/// which case quickfix list will be created.
+/// @param[in] list_arg Quickfix list contents.
+/// @param[in] action_arg Action to perform: append to an existing list,
+/// replace its content or create a new one.
+/// @param[in] title_arg New list title. Defaults to caller function name.
+/// @param[out] rettv Return value: 0 in case of success, -1 otherwise.
+static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ARG(2, 3)
{
- char_u *act;
+ char_u *title = NULL;
int action = ' ';
-
rettv->vval.v_number = -1;
- if (list_arg->v_type != VAR_LIST)
+ typval_T *list_arg = &args[0];
+ if (list_arg->v_type != VAR_LIST) {
EMSG(_(e_listreq));
- else {
- list_T *l = list_arg->vval.v_list;
+ return;
+ }
- if (action_arg->v_type == VAR_STRING) {
- act = get_tv_string_chk(action_arg);
- if (act == NULL)
- return; /* type error; errmsg already given */
- if (*act == 'a' || *act == 'r')
- action = *act;
- }
+ typval_T *action_arg = &args[1];
+ if (action_arg->v_type == VAR_UNKNOWN) {
+ // Option argument was not given.
+ goto skip_args;
+ } else if (action_arg->v_type != VAR_STRING) {
+ EMSG(_(e_strreq));
+ return;
+ }
+ char_u *act = get_tv_string_chk(action_arg);
+ if (*act == 'a' || *act == 'r') {
+ action = *act;
+ }
- if (l != NULL && set_errorlist(wp, l, action,
- (char_u *)(wp == NULL ? "setqflist()" : "setloclist()")) == OK)
- rettv->vval.v_number = 0;
+ typval_T *title_arg = &args[2];
+ if (title_arg->v_type == VAR_UNKNOWN) {
+ // Option argument was not given.
+ goto skip_args;
+ }
+ title = get_tv_string_chk(title_arg);
+ if (!title) {
+ // Type error. Error already printed by get_tv_string_chk().
+ return;
+ }
+
+skip_args:
+ if (!title) {
+ title = (char_u*)(wp ? "setloclist()" : "setqflist()");
+ }
+
+ list_T *l = list_arg->vval.v_list;
+ if (l && set_errorlist(wp, l, action, title) == OK) {
+ rettv->vval.v_number = 0;
}
}
@@ -15196,8 +14668,9 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = -1;
win = find_win_by_nr(&argvars[0], NULL);
- if (win != NULL)
- set_qf_ll_list(win, &argvars[1], &argvars[2], rettv);
+ if (win != NULL) {
+ set_qf_ll_list(win, &argvars[1], rettv);
+ }
}
/*
@@ -15243,8 +14716,8 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv)
int i = 0;
char_u buf[5];
dictitem_T *di;
- d = li->li_tv.vval.v_dict;
+ d = li->li_tv.vval.v_dict;
if (dict_find(d, (char_u *)"pattern", -1) == NULL) {
if (s == NULL) {
s = list_alloc();
@@ -15269,15 +14742,19 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv)
}
}
+ char_u *group = get_dict_string(d, (char_u *)"group", false);
+ int priority = get_dict_number(d, (char_u *)"priority");
+ int id = get_dict_number(d, (char_u *)"id");
+ char_u *conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
+ ? get_dict_string(d, (char_u *)"conceal",
+ false)
+ : NULL;
if (i == 0) {
- match_add(curwin, get_dict_string(d, (char_u *)"group", false),
+ match_add(curwin, group,
get_dict_string(d, (char_u *)"pattern", false),
- (int)get_dict_number(d, (char_u *)"priority"),
- (int)get_dict_number(d, (char_u *)"id"), NULL);
+ priority, id, NULL, conceal);
} else {
- match_add(curwin, get_dict_string(d, (char_u *)"group", false),
- NULL, (int)get_dict_number(d, (char_u *)"priority"),
- (int)get_dict_number(d, (char_u *)"id"), s);
+ match_add(curwin, group, NULL, priority, id, s, conceal);
list_unref(s);
s = NULL;
}
@@ -15301,25 +14778,30 @@ static void f_setpos(typval_T *argvars, typval_T *rettv)
name = get_tv_string_chk(argvars);
if (name != NULL) {
if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) {
- if (--pos.col < 0)
+ if (--pos.col < 0) {
pos.col = 0;
+ }
if (name[0] == '.' && name[1] == NUL) {
- /* set cursor */
+ // set cursor
if (fnum == curbuf->b_fnum) {
curwin->w_cursor = pos;
if (curswant >= 0) {
curwin->w_curswant = curswant - 1;
+ curwin->w_set_curswant = false;
}
check_cursor();
rettv->vval.v_number = 0;
- } else
+ } else {
EMSG(_(e_invarg));
+ }
} else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) {
- /* set mark */
- if (setmark_pos(name[1], &pos, fnum) == OK)
+ // set mark
+ if (setmark_pos(name[1], &pos, fnum) == OK) {
rettv->vval.v_number = 0;
- } else
+ }
+ } else {
EMSG(_(e_invarg));
+ }
}
}
}
@@ -15329,7 +14811,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv)
*/
static void f_setqflist(typval_T *argvars, typval_T *rettv)
{
- set_qf_ll_list(NULL, &argvars[0], &argvars[1], rettv);
+ set_qf_ll_list(NULL, argvars, rettv);
}
/*
@@ -15341,11 +14823,11 @@ static void f_setreg(typval_T *argvars, typval_T *rettv)
char_u *strregname;
char_u *stropt;
bool append = false;
- char_u yank_type;
+ MotionType yank_type;
long block_len;
block_len = -1;
- yank_type = MAUTO;
+ yank_type = kMTUnknown;
strregname = get_tv_string_chk(argvars);
rettv->vval.v_number = 1; /* FAIL is default */
@@ -15362,17 +14844,17 @@ static void f_setreg(typval_T *argvars, typval_T *rettv)
return; /* type error */
for (; *stropt != NUL; ++stropt)
switch (*stropt) {
- case 'a': case 'A': /* append */
+ case 'a': case 'A': // append
append = true;
break;
- case 'v': case 'c': /* character-wise selection */
- yank_type = MCHAR;
+ case 'v': case 'c': // character-wise selection
+ yank_type = kMTCharWise;
break;
- case 'V': case 'l': /* line-wise selection */
- yank_type = MLINE;
+ case 'V': case 'l': // line-wise selection
+ yank_type = kMTLineWise;
break;
- case 'b': case Ctrl_V: /* block-wise selection */
- yank_type = MBLOCK;
+ case 'b': case Ctrl_V: // block-wise selection
+ yank_type = kMTBlockWise;
if (ascii_isdigit(stropt[1])) {
++stropt;
block_len = getdigits_long(&stropt) - 1;
@@ -15503,26 +14985,32 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off)
varname = get_tv_string_chk(&argvars[off + 1]);
varp = &argvars[off + 2];
- if (win != NULL && varname != NULL && varp != NULL
- && switch_win(&save_curwin, &save_curtab, win, tp, TRUE) == OK) {
- if (*varname == '&') {
- long numval;
- char_u *strval;
- int error = FALSE;
-
- ++varname;
- numval = get_tv_number_chk(varp, &error);
- strval = get_tv_string_buf_chk(varp, nbuf);
- if (!error && strval != NULL)
- set_option_value(varname, numval, strval, OPT_LOCAL);
- } else {
- winvarname = xmalloc(STRLEN(varname) + 3);
- STRCPY(winvarname, "w:");
- STRCPY(winvarname + 2, varname);
- set_var(winvarname, varp, TRUE);
- xfree(winvarname);
+ if (win != NULL && varname != NULL && varp != NULL) {
+ bool need_switch_win = tp != curtab || win != curwin;
+ if (!need_switch_win
+ || switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) {
+ if (*varname == '&') {
+ long numval;
+ char_u *strval;
+ int error = false;
+
+ ++varname;
+ numval = get_tv_number_chk(varp, &error);
+ strval = get_tv_string_buf_chk(varp, nbuf);
+ if (!error && strval != NULL) {
+ set_option_value(varname, numval, strval, OPT_LOCAL);
+ }
+ } else {
+ winvarname = xmalloc(STRLEN(varname) + 3);
+ STRCPY(winvarname, "w:");
+ STRCPY(winvarname + 2, varname);
+ set_var(winvarname, varp, true);
+ xfree(winvarname);
+ }
+ }
+ if (need_switch_win) {
+ restore_win(save_curwin, save_curtab, true);
}
- restore_win(save_curwin, save_curtab, TRUE);
}
}
@@ -15592,6 +15080,8 @@ typedef struct {
static int item_compare_ic;
static bool item_compare_numeric;
+static bool item_compare_numbers;
+static bool item_compare_float;
static char_u *item_compare_func;
static dict_T *item_compare_selfdict;
static int item_compare_func_err;
@@ -15613,9 +15103,24 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
si2 = (sortItem_T *)s2;
typval_T *tv1 = &si1->item->li_tv;
typval_T *tv2 = &si2->item->li_tv;
- // tv2string() puts quotes around a string and allocates memory. Don't do
- // that for string variables. Use a single quote when comparing with a
- // non-string to do what the docs promise.
+
+ if (item_compare_numbers) {
+ long v1 = get_tv_number(tv1);
+ long v2 = get_tv_number(tv2);
+
+ return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
+ }
+
+ if (item_compare_float) {
+ float_T v1 = get_tv_float(tv1);
+ float_T v2 = get_tv_float(tv2);
+
+ return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
+ }
+
+ // encode_tv2string() puts quotes around a string and allocates memory. Don't
+ // do that for string variables. Use a single quote when comparing with
+ // a non-string to do what the docs promise.
if (tv1->v_type == VAR_STRING) {
if (tv2->v_type != VAR_STRING || item_compare_numeric) {
p1 = (char_u *)"'";
@@ -15623,7 +15128,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
p1 = tv1->vval.v_string;
}
} else {
- tofree1 = p1 = (char_u *) tv2string(tv1, NULL);
+ tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL);
}
if (tv2->v_type == VAR_STRING) {
if (tv1->v_type != VAR_STRING || item_compare_numeric) {
@@ -15632,7 +15137,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
p2 = tv2->vval.v_string;
}
} else {
- tofree2 = p2 = (char_u *) tv2string(tv2, NULL);
+ tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL);
}
if (p1 == NULL)
p1 = (char_u *)"";
@@ -15741,8 +15246,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");
} else {
l = argvars[0].vval.v_list;
- if (l == NULL || tv_check_lock(l->lv_lock,
- (char_u *)(sort ? _("sort() argument") : _("uniq() argument")))) {
+ if (l == NULL
+ || tv_check_lock(l->lv_lock,
+ (char_u *)(sort
+ ? N_("sort() argument")
+ : N_("uniq() argument")),
+ true)) {
return;
}
rettv->vval.v_list = l;
@@ -15755,6 +15264,8 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
item_compare_ic = FALSE;
item_compare_numeric = false;
+ item_compare_numbers = false;
+ item_compare_float = false;
item_compare_func = NULL;
item_compare_selfdict = NULL;
@@ -15776,6 +15287,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
if (STRCMP(item_compare_func, "n") == 0) {
item_compare_func = NULL;
item_compare_numeric = true;
+ } else if (STRCMP(item_compare_func, "N") == 0) {
+ item_compare_func = NULL;
+ item_compare_numbers = true;
+ } else if (STRCMP(item_compare_func, "f") == 0) {
+ item_compare_func = NULL;
+ item_compare_float = true;
} else if (STRCMP(item_compare_func, "i") == 0) {
item_compare_func = NULL;
item_compare_ic = TRUE;
@@ -15880,6 +15397,21 @@ static void f_uniq(typval_T *argvars, typval_T *rettv)
do_sort_uniq(argvars, rettv, false);
}
+//
+// "reltimefloat()" function
+//
+static void f_reltimefloat(typval_T *argvars , typval_T *rettv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ proftime_T tm;
+
+ rettv->v_type = VAR_FLOAT;
+ rettv->vval.v_float = 0;
+ if (list2proftime(&argvars[0], &tm) == OK) {
+ rettv->vval.v_float = ((float_T)tm) / 1000000000;
+ }
+}
+
/*
* "soundfold({word})" function
*/
@@ -16064,7 +15596,7 @@ static void f_str2float(typval_T *argvars, typval_T *rettv)
if (*p == '+')
p = skipwhite(p + 1);
- (void)string2float(p, &rettv->vval.v_float);
+ (void) string2float((char *) p, &rettv->vval.v_float);
rettv->v_type = VAR_FLOAT;
}
@@ -16195,7 +15727,7 @@ static void f_stridx(typval_T *argvars, typval_T *rettv)
static void f_string(typval_T *argvars, typval_T *rettv)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *) tv2string(&argvars[0], NULL);
+ rettv->vval.v_string = (char_u *) encode_tv2string(&argvars[0], NULL);
}
/*
@@ -16213,13 +15745,23 @@ static void f_strlen(typval_T *argvars, typval_T *rettv)
static void f_strchars(typval_T *argvars, typval_T *rettv)
{
char_u *s = get_tv_string(&argvars[0]);
+ int skipcc = 0;
varnumber_T len = 0;
+ int (*func_mb_ptr2char_adv)(char_u **pp);
- while (*s != NUL) {
- mb_cptr2char_adv(&s);
- ++len;
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ skipcc = get_tv_number_chk(&argvars[1], NULL);
+ }
+ if (skipcc < 0 || skipcc > 1) {
+ EMSG(_(e_invarg));
+ } else {
+ func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv;
+ while (*s != NUL) {
+ func_mb_ptr2char_adv(&s);
+ ++len;
+ }
+ rettv->vval.v_number = len;
}
- rettv->vval.v_number = len;
}
/*
@@ -16918,9 +16460,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
// Save the job id and pid in b:terminal_job_{id,pid}
Error err;
dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_id"),
- INTEGER_OBJ(rettv->vval.v_number), &err);
+ INTEGER_OBJ(rettv->vval.v_number), false, &err);
dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
- INTEGER_OBJ(pid), &err);
+ INTEGER_OBJ(pid), false, &err);
Terminal *term = terminal_open(topts);
data->term = term;
@@ -16953,6 +16495,119 @@ static void f_tanh(typval_T *argvars, typval_T *rettv)
float_op_wrapper(argvars, rettv, &tanh);
}
+
+/// "timer_start(timeout, callback, opts)" function
+static void f_timer_start(typval_T *argvars, typval_T *rettv)
+{
+ long timeout = get_tv_number(&argvars[0]);
+ timer_T *timer;
+ int repeat = 1;
+ dict_T *dict;
+
+ rettv->vval.v_number = -1;
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ if (argvars[2].v_type != VAR_DICT
+ || (dict = argvars[2].vval.v_dict) == NULL) {
+ EMSG2(_(e_invarg2), get_tv_string(&argvars[2]));
+ return;
+ }
+ if (dict_find(dict, (char_u *)"repeat", -1) != NULL) {
+ repeat = get_dict_number(dict, (char_u *)"repeat");
+ }
+ }
+
+ if (argvars[1].v_type != VAR_FUNC && argvars[1].v_type != VAR_STRING) {
+ EMSG2(e_invarg2, "funcref");
+ return;
+ }
+ ufunc_T *func = find_ufunc(argvars[1].vval.v_string);
+ if (!func) {
+ // Invalid function name. Error already reported by `find_ufunc`.
+ return;
+ }
+ func->uf_refcount++;
+
+ timer = xmalloc(sizeof *timer);
+ timer->stopped = false;
+ timer->repeat_count = repeat;
+ timer->timer_id = last_timer_id++;
+ timer->callback = func;
+
+ time_watcher_init(&loop, &timer->tw, timer);
+ timer->tw.events = queue_new_child(loop.events);
+ // if main loop is blocked, don't queue up multiple events
+ timer->tw.blockable = true;
+ time_watcher_start(&timer->tw, timer_due_cb, timeout,
+ timeout * (repeat != 1));
+
+ pmap_put(uint64_t)(timers, timer->timer_id, timer);
+ rettv->vval.v_number = timer->timer_id;
+}
+
+
+// "timer_stop(timerid)" function
+static void f_timer_stop(typval_T *argvars, typval_T *rettv)
+{
+ if (argvars[0].v_type != VAR_NUMBER) {
+ EMSG(_(e_number_exp));
+ return;
+ }
+
+ timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0]));
+
+ if (timer == NULL) {
+ return;
+ }
+
+ timer_stop(timer);
+}
+
+// invoked on the main loop
+static void timer_due_cb(TimeWatcher *tw, void *data)
+{
+ timer_T *timer = (timer_T *)data;
+ if (timer->stopped) {
+ return;
+ }
+ // if repeat was negative repeat forever
+ if (timer->repeat_count >= 0 && --timer->repeat_count == 0) {
+ timer_stop(timer);
+ }
+
+ typval_T argv[1];
+ init_tv(argv);
+ argv[0].v_type = VAR_NUMBER;
+ argv[0].vval.v_number = timer->timer_id;
+ typval_T rettv;
+
+ init_tv(&rettv);
+ call_user_func(timer->callback, ARRAY_SIZE(argv), argv, &rettv,
+ curwin->w_cursor.lnum, curwin->w_cursor.lnum, NULL);
+ clear_tv(&rettv);
+}
+
+static void timer_stop(timer_T *timer)
+{
+ if (timer->stopped) {
+ // avoid double free
+ return;
+ }
+ timer->stopped = true;
+ time_watcher_stop(&timer->tw);
+ time_watcher_close(&timer->tw, timer_free_cb);
+}
+
+// invoked on next event loop tick, so queue is empty
+static void timer_free_cb(TimeWatcher *tw, void *data)
+{
+ timer_T *timer = (timer_T *)data;
+ queue_free(timer->tw.events);
+ user_func_unref(timer->callback);
+ pmap_del(uint64_t)(timers, timer->timer_id);
+ xfree(timer);
+}
+
/*
* "tolower(string)" function
*/
@@ -17106,16 +16761,33 @@ static void f_trunc(typval_T *argvars, typval_T *rettv)
*/
static void f_type(typval_T *argvars, typval_T *rettv)
{
- int n;
+ int n = -1;
switch (argvars[0].v_type) {
- case VAR_NUMBER: n = 0; break;
- case VAR_STRING: n = 1; break;
- case VAR_FUNC: n = 2; break;
- case VAR_LIST: n = 3; break;
- case VAR_DICT: n = 4; break;
- case VAR_FLOAT: n = 5; break;
- default: EMSG2(_(e_intern2), "f_type()"); n = 0; break;
+ case VAR_NUMBER: n = 0; break;
+ case VAR_STRING: n = 1; break;
+ case VAR_FUNC: n = 2; break;
+ case VAR_LIST: n = 3; break;
+ case VAR_DICT: n = 4; break;
+ case VAR_FLOAT: n = 5; break;
+ case VAR_SPECIAL: {
+ switch (argvars[0].vval.v_special) {
+ case kSpecialVarTrue:
+ case kSpecialVarFalse: {
+ n = 6;
+ break;
+ }
+ case kSpecialVarNull: {
+ n = 7;
+ break;
+ }
+ }
+ break;
+ }
+ case VAR_UNKNOWN: {
+ EMSG2(_(e_intern2), "f_type(UNKNOWN)");
+ break;
+ }
}
rettv->vval.v_number = n;
}
@@ -17467,6 +17139,13 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = wp->w_width;
}
+/// "wordcount()" function
+static void f_wordcount(typval_T *argvars, typval_T *rettv)
+{
+ rettv_dict_alloc(rettv);
+ cursor_pos_info(rettv->vval.v_dict);
+}
+
/// "writefile()" function
static void f_writefile(typval_T *argvars, typval_T *rettv)
{
@@ -17694,21 +17373,28 @@ static int get_env_len(char_u **arg)
return len;
}
-/*
- * Get the length of the name of a function or internal variable.
- * "arg" is advanced to the first non-white character after the name.
- * Return 0 if something is wrong.
- */
-static int get_id_len(char_u **arg)
-{
- char_u *p;
+// Get the length of the name of a function or internal variable.
+// "arg" is advanced to the first non-white character after the name.
+// Return 0 if something is wrong.
+static int get_id_len(char_u **arg) {
+ char_u *p;
int len;
- /* Find the end of the name. */
- for (p = *arg; eval_isnamec(*p); ++p)
- ;
- if (p == *arg) /* no name found */
+ // Find the end of the name.
+ for (p = *arg; eval_isnamec(*p); p++) {
+ if (*p == ':') {
+ // "s:" is start of "s:var", but "n:" is not and can be used in
+ // slice "[n:]". Also "xx:" is not a namespace.
+ len = (int)(p - *arg);
+ if (len > 1
+ || (len == 1 && vim_strchr(namespace_char, **arg) == NULL)) {
+ break;
+ }
+ }
+ }
+ if (p == *arg) { // no name found
return 0;
+ }
len = (int)(p - *arg);
*arg = skipwhite(p);
@@ -17779,28 +17465,29 @@ static int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose)
return len;
}
-/*
- * Find the end of a variable or function name, taking care of magic braces.
- * If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the
- * start and end of the first magic braces item.
- * "flags" can have FNE_INCL_BR and FNE_CHECK_START.
- * Return a pointer to just after the name. Equal to "arg" if there is no
- * valid name.
- */
-static char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags)
+// Find the end of a variable or function name, taking care of magic braces.
+// If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the
+// start and end of the first magic braces item.
+// "flags" can have FNE_INCL_BR and FNE_CHECK_START.
+// Return a pointer to just after the name. Equal to "arg" if there is no
+// valid name.
+static char_u *find_name_end(char_u *arg, char_u **expr_start,
+ char_u **expr_end, int flags)
{
int mb_nest = 0;
int br_nest = 0;
- char_u *p;
+ char_u *p;
+ int len;
if (expr_start != NULL) {
*expr_start = NULL;
*expr_end = NULL;
}
- /* Quick check for valid starting character. */
- if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{')
+ // Quick check for valid starting character.
+ if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{') {
return arg;
+ }
for (p = arg; *p != NUL
&& (eval_isnamec(*p)
@@ -17815,30 +17502,44 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end
if (*p == NUL)
break;
} else if (*p == '"') {
- /* skip over "str\"ing" to avoid counting [ and ] inside it. */
- for (p = p + 1; *p != NUL && *p != '"'; mb_ptr_adv(p))
- if (*p == '\\' && p[1] != NUL)
+ // skip over "str\"ing" to avoid counting [ and ] inside it.
+ for (p = p + 1; *p != NUL && *p != '"'; mb_ptr_adv(p)) {
+ if (*p == '\\' && p[1] != NUL) {
++p;
- if (*p == NUL)
+ }
+ }
+ if (*p == NUL) {
break;
+ }
+ } else if (br_nest == 0 && mb_nest == 0 && *p == ':') {
+ // "s:" is start of "s:var", but "n:" is not and can be used in
+ // slice "[n:]". Also "xx:" is not a namespace. But {ns}: is. */
+ len = (int)(p - arg);
+ if ((len > 1 && p[-1] != '}')
+ || (len == 1 && vim_strchr(namespace_char, *arg) == NULL)) {
+ break;
+ }
}
if (mb_nest == 0) {
- if (*p == '[')
+ if (*p == '[') {
++br_nest;
- else if (*p == ']')
+ } else if (*p == ']') {
--br_nest;
+ }
}
if (br_nest == 0) {
if (*p == '{') {
mb_nest++;
- if (expr_start != NULL && *expr_start == NULL)
+ if (expr_start != NULL && *expr_start == NULL) {
*expr_start = p;
+ }
} else if (*p == '}') {
mb_nest--;
- if (expr_start != NULL && mb_nest == 0 && *expr_end == NULL)
+ if (expr_start != NULL && mb_nest == 0 && *expr_end == NULL) {
*expr_end = p;
+ }
}
}
}
@@ -17920,14 +17621,6 @@ static int eval_isnamec1(int c)
}
/*
- * Set number v: variable to "val".
- */
-void set_vim_var_nr(int idx, long val)
-{
- vimvars[idx].vv_nr = val;
-}
-
-/*
* Get number v: variable value.
*/
long get_vim_var_nr(int idx) FUNC_ATTR_PURE
@@ -17964,11 +17657,11 @@ dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE
*/
void set_vim_var_char(int c)
{
- char_u buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXBYTES + 1];
- if (has_mbyte)
- buf[(*mb_char2bytes)(c, buf)] = NUL;
- else {
+ if (has_mbyte) {
+ buf[(*mb_char2bytes)(c, (char_u *) buf)] = NUL;
+ } else {
buf[0] = c;
buf[1] = NUL;
}
@@ -17987,57 +17680,71 @@ void set_vcount(long count, long count1, int set_prevcount)
vimvars[VV_COUNT1].vv_nr = count1;
}
-/*
- * Set string v: variable to a copy of "val".
- */
-void set_vim_var_string (
- int idx,
- char_u *val,
- int len /* length of "val" to use or -1 (whole string) */
-)
+/// Set number v: variable to the given value
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in] val Value to set to.
+void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val)
{
- /* Need to do this (at least) once, since we can't initialize a union.
- * Will always be invoked when "v:progname" is set. */
- vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
+ vimvars[idx].vv_nr = val;
+}
+
+/// Set special v: variable to the given value
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in] val Value to set to.
+void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val)
+{
+ vimvars[idx].vv_special = val;
+}
+/// Set string v: variable to the given string
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in] val Value to set to. Will be copied.
+/// @param[in] len Legth of that value or -1 in which case strlen() will be
+/// used.
+void set_vim_var_string(const VimVarIndex idx, const char *const val,
+ const ptrdiff_t len)
+{
xfree(vimvars[idx].vv_str);
- if (val == NULL)
+ if (val == NULL) {
vimvars[idx].vv_str = NULL;
- else if (len == -1)
- vimvars[idx].vv_str = vim_strsave(val);
- else
- vimvars[idx].vv_str = vim_strnsave(val, len);
+ } else if (len == -1) {
+ vimvars[idx].vv_str = (char_u *) xstrdup(val);
+ } else {
+ vimvars[idx].vv_str = (char_u *) xstrndup(val, (size_t) len);
+ }
}
-/*
- * Set List v: variable to "val".
- */
-void set_vim_var_list(int idx, list_T *val)
+/// Set list v: variable to the given list
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in,out] val Value to set to. Reference count will be incremented.
+void set_vim_var_list(const VimVarIndex idx, list_T *const val)
{
list_unref(vimvars[idx].vv_list);
vimvars[idx].vv_list = val;
- if (val != NULL)
- ++val->lv_refcount;
+ if (val != NULL) {
+ val->lv_refcount++;
+ }
}
-/// Set Dictionary v: variable to "val".
-void set_vim_var_dict(int idx, dict_T *val) FUNC_ATTR_NONNULL_ALL
+/// Set Dictionary v: variable to the given dictionary
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in,out] val Value to set to. Reference count will be incremented.
+/// Also keys of the dictionary will be made read-only.
+void set_vim_var_dict(const VimVarIndex idx, dict_T *const val)
{
dict_unref(vimvars[idx].vv_dict);
+ vimvars[idx].vv_dict = val;
- // Set readonly
- int todo = (int)val->dv_hashtab.ht_used;
- for (hashitem_T *hi = val->dv_hashtab.ht_array; todo > 0 ; ++hi) {
- if (HASHITEM_EMPTY(hi)) {
- continue;
- }
-
- --todo;
- HI2DI(hi)->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
+ if (val != NULL) {
+ val->dv_refcount++;
+ // Set readonly
+ dict_set_keys_readonly(val);
}
-
- vimvars[idx].vv_dict = val;
- ++val->dv_refcount;
}
/*
@@ -18045,15 +17752,17 @@ void set_vim_var_dict(int idx, dict_T *val) FUNC_ATTR_NONNULL_ALL
*/
void set_reg_var(int c)
{
- char_u regname;
+ char regname;
- if (c == 0 || c == ' ')
+ if (c == 0 || c == ' ') {
regname = '"';
- else
+ } else {
regname = c;
- /* Avoid free/alloc when the value is already right. */
- if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c)
+ }
+ // Avoid free/alloc when the value is already right.
+ if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c) {
set_vim_var_string(VV_REG, &regname, 1);
+ }
}
/*
@@ -18155,10 +17864,11 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg)
static int
get_var_tv (
char_u *name,
- int len, /* length of "name" */
- typval_T *rettv, /* NULL when only checking existence */
- int verbose, /* may give error message */
- int no_autoload /* do not use script autoloading */
+ int len, // length of "name"
+ typval_T *rettv, // NULL when only checking existence
+ dictitem_T **dip, // non-NULL when typval's dict item is needed
+ int verbose, // may give error message
+ int no_autoload // do not use script autoloading
)
{
int ret = OK;
@@ -18184,8 +17894,12 @@ get_var_tv (
*/
else {
v = find_var(name, NULL, no_autoload);
- if (v != NULL)
+ if (v != NULL) {
tv = &v->di_tv;
+ if (dip != NULL) {
+ *dip = v;
+ }
+ }
}
if (tv == NULL) {
@@ -18278,25 +17992,23 @@ void free_tv(typval_T *varp)
{
if (varp != NULL) {
switch (varp->v_type) {
- case VAR_FUNC:
- func_unref(varp->vval.v_string);
- /*FALLTHROUGH*/
- case VAR_STRING:
- xfree(varp->vval.v_string);
- break;
- case VAR_LIST:
- list_unref(varp->vval.v_list);
- break;
- case VAR_DICT:
- dict_unref(varp->vval.v_dict);
- break;
- case VAR_NUMBER:
- case VAR_FLOAT:
- case VAR_UNKNOWN:
- break;
- default:
- EMSG2(_(e_intern2), "free_tv()");
- break;
+ case VAR_FUNC:
+ func_unref(varp->vval.v_string);
+ // FALLTHROUGH
+ case VAR_STRING:
+ xfree(varp->vval.v_string);
+ break;
+ case VAR_LIST:
+ list_unref(varp->vval.v_list);
+ break;
+ case VAR_DICT:
+ dict_unref(varp->vval.v_dict);
+ break;
+ case VAR_SPECIAL:
+ case VAR_NUMBER:
+ case VAR_FLOAT:
+ case VAR_UNKNOWN:
+ break;
}
xfree(varp);
}
@@ -18334,10 +18046,11 @@ void clear_tv(typval_T *varp)
case VAR_FLOAT:
varp->vval.v_float = 0.0;
break;
+ case VAR_SPECIAL:
+ varp->vval.v_special = kSpecialVarFalse;
+ break;
case VAR_UNKNOWN:
break;
- default:
- EMSG2(_(e_intern2), "clear_tv()");
}
varp->v_lock = 0;
}
@@ -18392,8 +18105,19 @@ long get_tv_number_chk(typval_T *varp, int *denote)
case VAR_DICT:
EMSG(_("E728: Using a Dictionary as a Number"));
break;
- default:
- EMSG2(_(e_intern2), "get_tv_number()");
+ case VAR_SPECIAL:
+ switch (varp->vval.v_special) {
+ case kSpecialVarTrue: {
+ return 1;
+ }
+ case kSpecialVarFalse:
+ case kSpecialVarNull: {
+ return 0;
+ }
+ }
+ break;
+ case VAR_UNKNOWN:
+ EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)");
break;
}
if (denote == NULL) {
@@ -18405,6 +18129,33 @@ long get_tv_number_chk(typval_T *varp, int *denote)
return n;
}
+static float_T get_tv_float(typval_T *varp)
+{
+ switch (varp->v_type) {
+ case VAR_NUMBER:
+ return (float_T)(varp->vval.v_number);
+ case VAR_FLOAT:
+ return varp->vval.v_float;
+ break;
+ case VAR_FUNC:
+ EMSG(_("E891: Using a Funcref as a Float"));
+ break;
+ case VAR_STRING:
+ EMSG(_("E892: Using a String as a Float"));
+ break;
+ case VAR_LIST:
+ EMSG(_("E893: Using a List as a Float"));
+ break;
+ case VAR_DICT:
+ EMSG(_("E894: Using a Dictionary as a Float"));
+ break;
+ default:
+ EMSG2(_(e_intern2), "get_tv_float()");
+ break;
+ }
+ return 0;
+}
+
/*
* Get the lnum from the first argument.
* Also accepts ".", "$", etc., but that only works for the current buffer.
@@ -18498,8 +18249,11 @@ static char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf)
if (varp->vval.v_string != NULL)
return varp->vval.v_string;
return (char_u *)"";
- default:
- EMSG2(_(e_intern2), "get_tv_string_buf()");
+ case VAR_SPECIAL:
+ STRCPY(buf, encode_special_var_names[varp->vval.v_special]);
+ return buf;
+ case VAR_UNKNOWN:
+ EMSG(_("E908: using an invalid value as a String"));
break;
}
return NULL;
@@ -18569,6 +18323,25 @@ static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, in
return HI2DI(hi);
}
+// Get function call environment based on backtrace debug level
+static funccall_T *get_funccal(void)
+{
+ funccall_T *funccal = current_funccal;
+ if (debug_backtrace_level > 0) {
+ for (int i = 0; i < debug_backtrace_level; i++) {
+ funccall_T *temp_funccal = funccal->caller;
+ if (temp_funccal) {
+ funccal = temp_funccal;
+ } else {
+ // backtrace level overflow. reset to max
+ debug_backtrace_level = i;
+ }
+ }
+ }
+
+ return funccal;
+}
+
// Find the dict and hashtable used for a variable name. Set "varname" to the
// start of name without ':'.
static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
@@ -18576,6 +18349,9 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
hashitem_T *hi;
*d = NULL;
+ if (name[0] == NUL) {
+ return NULL;
+ }
if (name[1] != ':') {
// name has implicit scope
if (name[0] == ':' || name[0] == AUTOLOAD_CHAR) {
@@ -18590,7 +18366,11 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
return &compat_hashtab;
}
- *d = current_funccal ? &current_funccal->l_vars : &globvardict;
+ if (current_funccal == NULL) {
+ *d = &globvardict;
+ } else {
+ *d = &get_funccal()->l_vars; // l: variable
+ }
goto end;
}
@@ -18612,9 +18392,9 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
} else if (*name == 'v') { // v: variable
*d = &vimvardict;
} else if (*name == 'a' && current_funccal != NULL) { // function argument
- *d = &current_funccal->l_avars;
+ *d = &get_funccal()->l_avars;
} else if (*name == 'l' && current_funccal != NULL) { // local variable
- *d = &current_funccal->l_vars;
+ *d = &get_funccal()->l_vars;
} else if (*name == 's' // script variable
&& current_SID > 0 && current_SID <= ga_scripts.ga_len) {
*d = &SCRIPT_SV(current_SID)->sv_dict;
@@ -18625,6 +18405,7 @@ end:
}
// Find the hashtab used for a variable name.
+// Return NULL if the name is not valid.
// Set "varname" to the start of name without ':'.
static hashtab_T *find_var_ht(uint8_t *name, uint8_t **varname)
{
@@ -18766,7 +18547,7 @@ static void delete_var(hashtab_T *ht, hashitem_T *hi)
*/
static void list_one_var(dictitem_T *v, char_u *prefix, int *first)
{
- char_u *s = (char_u *) echo_string(&v->di_tv, NULL);
+ char_u *s = (char_u *) encode_tv2echo(&v->di_tv, NULL);
list_one_var_a(prefix, v->di_key, v->di_tv.v_type,
s == NULL ? (char_u *)"" : s, first);
xfree(s);
@@ -18848,10 +18629,11 @@ set_var (
return;
if (v != NULL) {
- /* existing variable, need to clear the value */
- if (var_check_ro(v->di_flags, name)
- || tv_check_lock(v->di_tv.v_lock, name))
+ // existing variable, need to clear the value
+ if (var_check_ro(v->di_flags, name, false)
+ || tv_check_lock(v->di_tv.v_lock, name, false)) {
return;
+ }
if (v->di_tv.v_type != tv->v_type
&& !((v->di_tv.v_type == VAR_STRING
|| v->di_tv.v_type == VAR_NUMBER)
@@ -18866,10 +18648,8 @@ set_var (
return;
}
- /*
- * Handle setting internal v: variables separately: we don't change
- * the type.
- */
+ // Handle setting internal v: variables separately where needed to
+ // prevent changing the type.
if (ht == &vimvarht) {
if (v->di_tv.v_type == VAR_STRING) {
xfree(v->di_tv.vval.v_string);
@@ -18880,9 +18660,8 @@ set_var (
v->di_tv.vval.v_string = tv->vval.v_string;
tv->vval.v_string = NULL;
}
- } else if (v->di_tv.v_type != VAR_NUMBER)
- EMSG2(_(e_intern2), "set_var()");
- else {
+ return;
+ } else if (v->di_tv.v_type == VAR_NUMBER) {
v->di_tv.vval.v_number = get_tv_number(tv);
if (STRCMP(varname, "searchforward") == 0)
set_search_direction(v->di_tv.vval.v_number ? '/' : '?');
@@ -18890,8 +18669,10 @@ set_var (
no_hlsearch = !v->di_tv.vval.v_number;
redraw_all_later(SOME_VALID);
}
+ return;
+ } else if (v->di_tv.v_type != tv->v_type) {
+ EMSG2(_(e_intern2), "set_var()");
}
- return;
}
if (watched) {
@@ -18936,34 +18717,31 @@ set_var (
}
}
-/*
- * Return TRUE if di_flags "flags" indicates variable "name" is read-only.
- * Also give an error message.
- */
-static int var_check_ro(int flags, char_u *name)
+// Return true if di_flags "flags" indicates variable "name" is read-only.
+// Also give an error message.
+static bool var_check_ro(int flags, char_u *name, bool use_gettext)
{
if (flags & DI_FLAGS_RO) {
- EMSG2(_(e_readonlyvar), name);
- return TRUE;
+ EMSG2(_(e_readonlyvar), use_gettext ? (char_u *)_(name) : name);
+ return true;
}
if ((flags & DI_FLAGS_RO_SBX) && sandbox) {
- EMSG2(_(e_readonlysbx), name);
- return TRUE;
+ EMSG2(_(e_readonlysbx), use_gettext ? (char_u *)_(name) : name);
+ return true;
}
- return FALSE;
+ return false;
}
-/*
- * Return TRUE if di_flags "flags" indicates variable "name" is fixed.
- * Also give an error message.
- */
-static int var_check_fixed(int flags, char_u *name)
+// Return true if di_flags "flags" indicates variable "name" is fixed.
+// Also give an error message.
+static bool var_check_fixed(int flags, char_u *name, bool use_gettext)
{
if (flags & DI_FLAGS_FIX) {
- EMSG2(_("E795: Cannot delete variable %s"), name);
- return TRUE;
+ EMSG2(_("E795: Cannot delete variable %s"),
+ use_gettext ? (char_u *)_(name) : name);
+ return true;
}
- return FALSE;
+ return false;
}
/*
@@ -19011,23 +18789,28 @@ static int valid_varname(char_u *varname)
return TRUE;
}
-/*
- * Return TRUE if typeval "tv" is set to be locked (immutable).
- * Also give an error message, using "name".
- */
-static int tv_check_lock(int lock, char_u *name)
+// Return true if typeval "tv" is set to be locked (immutable).
+// Also give an error message, using "name" or _("name") when use_gettext is
+// true.
+static bool tv_check_lock(int lock, char_u *name, bool use_gettext)
{
if (lock & VAR_LOCKED) {
EMSG2(_("E741: Value is locked: %s"),
- name == NULL ? (char_u *)_("Unknown") : name);
- return TRUE;
+ name == NULL
+ ? (char_u *)_("Unknown")
+ : use_gettext ? (char_u *)_(name)
+ : name);
+ return true;
}
if (lock & VAR_FIXED) {
EMSG2(_("E742: Cannot change value of %s"),
- name == NULL ? (char_u *)_("Unknown") : name);
- return TRUE;
+ name == NULL
+ ? (char_u *)_("Unknown")
+ : use_gettext ? (char_u *)_(name)
+ : name);
+ return true;
}
- return FALSE;
+ return false;
}
/*
@@ -19041,42 +18824,34 @@ void copy_tv(typval_T *from, typval_T *to)
{
to->v_type = from->v_type;
to->v_lock = 0;
+ memmove(&to->vval, &from->vval, sizeof(to->vval));
switch (from->v_type) {
- case VAR_NUMBER:
- to->vval.v_number = from->vval.v_number;
- break;
- case VAR_FLOAT:
- to->vval.v_float = from->vval.v_float;
- break;
- case VAR_STRING:
- case VAR_FUNC:
- if (from->vval.v_string == NULL)
- to->vval.v_string = NULL;
- else {
- to->vval.v_string = vim_strsave(from->vval.v_string);
- if (from->v_type == VAR_FUNC)
- func_ref(to->vval.v_string);
- }
- break;
- case VAR_LIST:
- if (from->vval.v_list == NULL)
- to->vval.v_list = NULL;
- else {
- to->vval.v_list = from->vval.v_list;
- ++to->vval.v_list->lv_refcount;
- }
- break;
- case VAR_DICT:
- if (from->vval.v_dict == NULL)
- to->vval.v_dict = NULL;
- else {
- to->vval.v_dict = from->vval.v_dict;
- ++to->vval.v_dict->dv_refcount;
- }
- break;
- default:
- EMSG2(_(e_intern2), "copy_tv()");
- break;
+ case VAR_NUMBER:
+ case VAR_FLOAT:
+ case VAR_SPECIAL:
+ break;
+ case VAR_STRING:
+ case VAR_FUNC:
+ if (from->vval.v_string != NULL) {
+ to->vval.v_string = vim_strsave(from->vval.v_string);
+ if (from->v_type == VAR_FUNC) {
+ func_ref(to->vval.v_string);
+ }
+ }
+ break;
+ case VAR_LIST:
+ if (from->vval.v_list != NULL) {
+ to->vval.v_list->lv_refcount++;
+ }
+ break;
+ case VAR_DICT:
+ if (from->vval.v_dict != NULL) {
+ to->vval.v_dict->dv_refcount++;
+ }
+ break;
+ case VAR_UNKNOWN:
+ EMSG2(_(e_intern2), "copy_tv(UNKNOWN)");
+ break;
}
}
@@ -19116,6 +18891,7 @@ int var_item_copy(const vimconv_T *const conv,
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_FUNC:
+ case VAR_SPECIAL:
copy_tv(from, to);
break;
case VAR_STRING:
@@ -19162,8 +18938,8 @@ int var_item_copy(const vimconv_T *const conv,
if (to->vval.v_dict == NULL)
ret = FAIL;
break;
- default:
- EMSG2(_(e_intern2), "var_item_copy()");
+ case VAR_UNKNOWN:
+ EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)");
ret = FAIL;
}
--recurse;
@@ -19218,7 +18994,7 @@ void ex_echo(exarg_T *eap)
}
} else if (eap->cmdidx == CMD_echo)
msg_puts_attr((char_u *)" ", echo_attr);
- char_u *tofree = p = (char_u *) echo_string(&rettv, NULL);
+ char_u *tofree = p = (char_u *) encode_tv2echo(&rettv, NULL);
if (p != NULL) {
for (; *p != NUL && !got_int; ++p) {
if (*p == '\n' || *p == '\r' || *p == TAB) {
@@ -19620,7 +19396,10 @@ void ex_function(exarg_T *eap)
break;
}
}
- ++p; /* skip the ')' */
+ if (*p != ')') {
+ goto erret;
+ }
+ ++p; // skip the ')'
/* find extra arguments "range", "dict" and "abort" */
for (;; ) {
@@ -19750,9 +19529,10 @@ void ex_function(exarg_T *eap)
if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
|| (p[0] == 'i'
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
- && (!ASCII_ISALPHA(p[2]) ||
- (p[2] == 's'))))))
+ && (!ASCII_ISALPHA(p[2])
+ || (p[2] == 's')))))) {
skip_until = vim_strsave((char_u *)".");
+ }
// Check for ":python <<EOF", ":lua <<EOF", etc.
arg = skipwhite(skiptowhite(p));
@@ -19844,13 +19624,14 @@ void ex_function(exarg_T *eap)
goto erret;
}
if (fudi.fd_di == NULL) {
- /* Can't add a function to a locked dictionary */
- if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg))
+ if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg, false)) {
+ // Can't add a function to a locked dictionary
goto erret;
- }
- /* Can't change an existing function if it is locked */
- else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg))
+ }
+ } else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, false)) {
+ // Can't change an existing function if it is locked
goto erret;
+ }
/* Give the function a sequential number. Can only be used with a
* Funcref! */
@@ -20018,11 +19799,12 @@ trans_function_name (
*pp = end;
} else {
if (!skip && !(flags & TFN_QUIET) && (fdp == NULL
- || lv.ll_dict == NULL ||
- fdp->fd_newkey == NULL))
+ || lv.ll_dict == NULL
+ || fdp->fd_newkey == NULL)) {
EMSG(_(e_funcref));
- else
+ } else {
*pp = end;
+ }
name = NULL;
}
goto theend;
@@ -20828,14 +20610,25 @@ call_user_func (
save_sourcing_name = sourcing_name;
save_sourcing_lnum = sourcing_lnum;
sourcing_lnum = 1;
- sourcing_name = xmalloc((save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name))
- + STRLEN(fp->uf_name) + 13);
+ // need space for new sourcing_name:
+ // * save_sourcing_name
+ // * "["number"].." or "function "
+ // * "<SNR>" + fp->uf_name - 3
+ // * terminating NUL
+ size_t len = (save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name))
+ + STRLEN(fp->uf_name) + 27;
+ sourcing_name = xmalloc(len);
{
if (save_sourcing_name != NULL
- && STRNCMP(save_sourcing_name, "function ", 9) == 0)
- sprintf((char *)sourcing_name, "%s..", save_sourcing_name);
- else
+ && STRNCMP(save_sourcing_name, "function ", 9) == 0) {
+ vim_snprintf((char *)sourcing_name,
+ len,
+ "%s[%" PRId64 "]..",
+ save_sourcing_name,
+ (int64_t)save_sourcing_lnum);
+ } else {
STRCPY(sourcing_name, "function ");
+ }
cat_func_name(sourcing_name + STRLEN(sourcing_name), fp);
if (p_verbose >= 12) {
@@ -20855,10 +20648,10 @@ call_user_func (
msg_outnum((long)argvars[i].vval.v_number);
} else {
// Do not want errors such as E724 here.
- ++emsg_off;
- char_u *s = (char_u *) tv2string(&argvars[i], NULL);
+ emsg_off++;
+ char_u *s = (char_u *) encode_tv2string(&argvars[i], NULL);
char_u *tofree = s;
- --emsg_off;
+ emsg_off--;
if (s != NULL) {
if (vim_strsize(s) > MSG_BUF_CLEN) {
trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
@@ -20911,9 +20704,9 @@ call_user_func (
--RedrawingDisabled;
- /* when the function was aborted because of an error, return -1 */
- if ((did_emsg &&
- (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) {
+ // when the function was aborted because of an error, return -1
+ if ((did_emsg
+ && (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) {
clear_tv(rettv);
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = -1;
@@ -20949,10 +20742,10 @@ call_user_func (
// The value may be very long. Skip the middle part, so that we
// have some idea how it starts and ends. smsg() would always
// truncate it at the end. Don't want errors such as E724 here.
- ++emsg_off;
- char_u *s = (char_u *) tv2string(fc->rettv, NULL);
+ emsg_off++;
+ char_u *s = (char_u *) encode_tv2string(fc->rettv, NULL);
char_u *tofree = s;
- --emsg_off;
+ emsg_off--;
if (s != NULL) {
if (vim_strsize(s) > MSG_BUF_CLEN) {
trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
@@ -21223,7 +21016,7 @@ char_u *get_return_cmd(void *rettv)
char_u *tofree = NULL;
if (rettv != NULL) {
- tofree = s = (char_u *) echo_string((typval_T *) rettv, NULL);
+ tofree = s = (char_u *) encode_tv2echo((typval_T *) rettv, NULL);
}
if (s == NULL) {
s = (char_u *)"";
@@ -21802,7 +21595,15 @@ repeat:
}
if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') {
+ // vim_strsave_shellescape() needs a NUL terminated string.
+ c = (*fnamep)[*fnamelen];
+ if (c != NUL) {
+ (*fnamep)[*fnamelen] = NUL;
+ }
p = vim_strsave_shellescape(*fnamep, false, false);
+ if (c != NUL) {
+ (*fnamep)[*fnamelen] = c;
+ }
xfree(*bufp);
*bufp = *fnamep = p;
*fnamelen = STRLEN(p);
@@ -22098,10 +21899,9 @@ static void on_process_exit(Process *proc, int status, void *d)
TerminalJobData *data = d;
if (data->term && !data->exited) {
data->exited = true;
- char msg[22];
+ char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN];
snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status);
terminal_close(data->term, msg);
- apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, curbuf);
}
if (data->status_ptr) {
@@ -22124,6 +21924,18 @@ static void term_resize(uint16_t width, uint16_t height, void *d)
pty_process_resize(&data->proc.pty, width, height);
}
+static inline void term_delayed_free(void **argv)
+{
+ TerminalJobData *j = argv[0];
+ if (j->in.pending_reqs || j->out.pending_reqs || j->err.pending_reqs) {
+ queue_put(j->events, term_delayed_free, 1, j);
+ return;
+ }
+
+ terminal_destroy(j->term);
+ term_job_data_decref(j);
+}
+
static void term_close(void *d)
{
TerminalJobData *data = d;
@@ -22131,8 +21943,7 @@ static void term_close(void *d)
data->exited = true;
process_stop((Process *)&data->proc);
}
- terminal_destroy(data->term);
- term_job_data_decref(d);
+ queue_put(data->events, term_delayed_free, 1, data);
}
static void term_job_data_decref(TerminalJobData *data)
@@ -22207,6 +22018,7 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
if (argvars[0].v_type != VAR_STRING) {
EMSG(_(e_invarg));
+ return;
}
list_T *args = list_alloc();
@@ -22379,4 +22191,3 @@ static bool is_watched(dict_T *d)
{
return d && !QUEUE_EMPTY(&d->watchers);
}
-
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 79a1341d98..d6800afd52 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -1,12 +1,17 @@
#ifndef NVIM_EVAL_H
#define NVIM_EVAL_H
-#include <msgpack.h>
-
#include "nvim/profile.h"
+#include "nvim/hashtab.h" // For hashtab_T
+#include "nvim/garray.h" // For garray_T
+#include "nvim/buffer_defs.h" // For scid_T
+#include "nvim/ex_cmds_defs.h" // For exarg_T
+
+#define COPYID_INC 2
+#define COPYID_MASK (~0x1)
// All user-defined functions are found in this hashtable.
-EXTERN hashtab_T func_hashtab;
+extern hashtab_T func_hashtab;
// Structure to hold info for a user function.
typedef struct ufunc ufunc_T;
@@ -46,8 +51,8 @@ EXTERN ufunc_T dumuf;
#define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf)))
#define HI2UF(hi) HIKEY2UF((hi)->hi_key)
-/* Defines for Vim variables. These must match vimvars[] in eval.c! */
-enum {
+/// Defines for Vim variables
+typedef enum {
VV_COUNT,
VV_COUNT1,
VV_PREVCOUNT,
@@ -113,15 +118,36 @@ enum {
VV_OPTION_TYPE,
VV_ERRORS,
VV_MSGPACK_TYPES,
- VV_LEN, /* number of v: vars */
-};
+ VV_EVENT,
+ VV_FALSE,
+ VV_TRUE,
+ VV_NULL,
+ VV__NULL_LIST, // List with NULL value. For test purposes only.
+ VV__NULL_DICT, // Dictionary with NULL value. For test purposes only.
+} VimVarIndex;
+
+/// All recognized msgpack types
+typedef enum {
+ kMPNil,
+ kMPBoolean,
+ kMPInteger,
+ kMPFloat,
+ kMPString,
+ kMPBinary,
+ kMPArray,
+ kMPMap,
+ kMPExt,
+#define LAST_MSGPACK_TYPE kMPExt
+} MessagePackType;
+
+/// Array mapping values from MessagePackType to corresponding list pointers
+extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1];
+
+#undef LAST_MSGPACK_TYPE
/// Maximum number of function arguments
#define MAX_FUNC_ARGS 20
-int vim_to_msgpack(msgpack_packer *const, typval_T *const,
- const char *const objname);
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.h.generated.h"
#endif
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
new file mode 100644
index 0000000000..0774ef515f
--- /dev/null
+++ b/src/nvim/eval/decode.c
@@ -0,0 +1,1116 @@
+#include <stddef.h>
+
+#include <msgpack.h>
+
+#include "nvim/eval_defs.h"
+#include "nvim/eval.h"
+#include "nvim/eval/encode.h"
+#include "nvim/ascii.h"
+#include "nvim/message.h"
+#include "nvim/charset.h" // vim_str2nr
+#include "nvim/lib/kvec.h"
+#include "nvim/vim.h" // OK, FAIL
+
+/// Helper structure for container_struct
+typedef struct {
+ size_t stack_index; ///< Index of current container in stack.
+ list_T *special_val; ///< _VAL key contents for special maps.
+ ///< When container is not a special dictionary it is
+ ///< NULL.
+ const char *s; ///< Location where container starts.
+ typval_T container; ///< Container. Either VAR_LIST, VAR_DICT or VAR_LIST
+ ///< which is _VAL from special dictionary.
+} ContainerStackItem;
+
+/// Helper structure for values struct
+typedef struct {
+ bool is_special_string; ///< Indicates that current value is a special
+ ///< dictionary with string.
+ bool didcomma; ///< True if previous token was comma.
+ bool didcolon; ///< True if previous token was colon.
+ typval_T val; ///< Actual value.
+} ValuesStackItem;
+
+/// Vector containing values not yet saved in any container
+typedef kvec_t(ValuesStackItem) ValuesStack;
+
+/// Vector containing containers, each next container is located inside previous
+typedef kvec_t(ContainerStackItem) ContainerStack;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/decode.c.generated.h"
+#endif
+
+/// Create special dictionary
+///
+/// @param[out] rettv Location where created dictionary will be saved.
+/// @param[in] type Type of the dictionary.
+/// @param[in] val Value associated with the _VAL key.
+static inline void create_special_dict(typval_T *const rettv,
+ const MessagePackType type,
+ typval_T val)
+ FUNC_ATTR_NONNULL_ALL
+{
+ dict_T *const dict = dict_alloc();
+ dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE");
+ type_di->di_tv.v_type = VAR_LIST;
+ type_di->di_tv.v_lock = VAR_UNLOCKED;
+ type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type];
+ type_di->di_tv.vval.v_list->lv_refcount++;
+ dict_add(dict, type_di);
+ dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL");
+ val_di->di_tv = val;
+ dict_add(dict, val_di);
+ dict->dv_refcount++;
+ *rettv = (typval_T) {
+ .v_type = VAR_DICT,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_dict = dict },
+ };
+}
+
+#define DICT_LEN(dict) (dict)->dv_hashtab.ht_used
+
+/// Helper function used for working with stack vectors used by JSON decoder
+///
+/// @param[in,out] obj New object. Will either be put into the stack (and,
+/// probably, also inside container) or freed.
+/// @param[out] stack Object stack.
+/// @param[out] container_stack Container objects stack.
+/// @param[in,out] pp Position in string which is currently being parsed. Used
+/// for error reporting and is also set when decoding is
+/// restarted due to the necessity of converting regular
+/// dictionary to a special map.
+/// @param[out] next_map_special Is set to true when dictionary needs to be
+/// converted to a special map, otherwise not
+/// touched. Indicates that decoding has been
+/// restarted.
+/// @param[out] didcomma True if previous token was comma. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+/// @param[out] didcolon True if previous token was colon. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+///
+/// @return OK in case of success, FAIL in case of error.
+static inline int json_decoder_pop(ValuesStackItem obj,
+ ValuesStack *const stack,
+ ContainerStack *const container_stack,
+ const char **const pp,
+ bool *const next_map_special,
+ bool *const didcomma,
+ bool *const didcolon)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (kv_size(*container_stack) == 0) {
+ kv_push(ValuesStackItem, *stack, obj);
+ return OK;
+ }
+ ContainerStackItem last_container = kv_last(*container_stack);
+ const char *val_location = *pp;
+ if (obj.val.v_type == last_container.container.v_type
+ // vval.v_list and vval.v_dict should have the same size and offset
+ && ((void *) obj.val.vval.v_list
+ == (void *) last_container.container.vval.v_list)) {
+ (void) kv_pop(*container_stack);
+ val_location = last_container.s;
+ last_container = kv_last(*container_stack);
+ }
+ if (last_container.container.v_type == VAR_LIST) {
+ if (last_container.container.vval.v_list->lv_len != 0
+ && !obj.didcomma) {
+ EMSG2(_("E474: Expected comma before list item: %s"), val_location);
+ clear_tv(&obj.val);
+ return FAIL;
+ }
+ assert(last_container.special_val == NULL);
+ listitem_T *obj_li = listitem_alloc();
+ obj_li->li_tv = obj.val;
+ list_append(last_container.container.vval.v_list, obj_li);
+ } else if (last_container.stack_index == kv_size(*stack) - 2) {
+ if (!obj.didcolon) {
+ EMSG2(_("E474: Expected colon before dictionary value: %s"),
+ val_location);
+ clear_tv(&obj.val);
+ return FAIL;
+ }
+ ValuesStackItem key = kv_pop(*stack);
+ if (last_container.special_val == NULL) {
+ // These cases should have already been handled.
+ assert(!(key.is_special_string
+ || key.val.vval.v_string == NULL
+ || *key.val.vval.v_string == NUL));
+ dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string);
+ clear_tv(&key.val);
+ if (dict_add(last_container.container.vval.v_dict, obj_di)
+ == FAIL) {
+ assert(false);
+ }
+ obj_di->di_tv = obj.val;
+ } else {
+ list_T *const kv_pair = list_alloc();
+ list_append_list(last_container.special_val, kv_pair);
+ listitem_T *const key_li = listitem_alloc();
+ key_li->li_tv = key.val;
+ list_append(kv_pair, key_li);
+ listitem_T *const val_li = listitem_alloc();
+ val_li->li_tv = obj.val;
+ list_append(kv_pair, val_li);
+ }
+ } else {
+ // Object with key only
+ if (!obj.is_special_string && obj.val.v_type != VAR_STRING) {
+ EMSG2(_("E474: Expected string key: %s"), *pp);
+ clear_tv(&obj.val);
+ return FAIL;
+ } else if (!obj.didcomma
+ && (last_container.special_val == NULL
+ && (DICT_LEN(last_container.container.vval.v_dict) != 0))) {
+ EMSG2(_("E474: Expected comma before dictionary key: %s"), val_location);
+ clear_tv(&obj.val);
+ return FAIL;
+ }
+ // Handle empty key and key represented as special dictionary
+ if (last_container.special_val == NULL
+ && (obj.is_special_string
+ || obj.val.vval.v_string == NULL
+ || *obj.val.vval.v_string == NUL
+ || dict_find(last_container.container.vval.v_dict,
+ obj.val.vval.v_string, -1))) {
+ clear_tv(&obj.val);
+
+ // Restart
+ (void) kv_pop(*container_stack);
+ ValuesStackItem last_container_val =
+ kv_A(*stack, last_container.stack_index);
+ while (kv_size(*stack) > last_container.stack_index) {
+ clear_tv(&(kv_pop(*stack).val));
+ }
+ *pp = last_container.s;
+ *didcomma = last_container_val.didcomma;
+ *didcolon = last_container_val.didcolon;
+ *next_map_special = true;
+ return OK;
+ }
+ kv_push(ValuesStackItem, *stack, obj);
+ }
+ return OK;
+}
+
+#define LENP(p, e) \
+ ((int) ((e) - (p))), (p)
+#define OBJ(obj_tv, is_sp_string, didcomma_, didcolon_) \
+ ((ValuesStackItem) { \
+ .is_special_string = (is_sp_string), \
+ .val = (obj_tv), \
+ .didcomma = (didcomma_), \
+ .didcolon = (didcolon_), \
+ })
+
+#define POP(obj_tv, is_sp_string) \
+ do { \
+ if (json_decoder_pop(OBJ(obj_tv, is_sp_string, *didcomma, *didcolon), \
+ stack, container_stack, \
+ &p, next_map_special, didcomma, didcolon) \
+ == FAIL) { \
+ goto parse_json_string_fail; \
+ } \
+ if (*next_map_special) { \
+ goto parse_json_string_ret; \
+ } \
+ } while (0)
+
+/// Parse JSON double-quoted string
+///
+/// @param[in] conv Defines conversion necessary to convert UTF-8 string to
+/// &encoding.
+/// @param[in] buf Buffer being converted.
+/// @param[in] buf_len Length of the buffer.
+/// @param[in,out] pp Pointer to the start of the string. Must point to '"'.
+/// Is advanced to the closing '"'. Also see
+/// json_decoder_pop(), it may set pp to another location
+/// and alter next_map_special, didcomma and didcolon.
+/// @param[out] stack Object stack.
+/// @param[out] container_stack Container objects stack.
+/// @param[out] next_map_special Is set to true when dictionary is converted
+/// to a special map, otherwise not touched.
+/// @param[out] didcomma True if previous token was comma. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+/// @param[out] didcolon True if previous token was colon. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+///
+/// @return OK in case of success, FAIL in case of error.
+static inline int parse_json_string(vimconv_T *const conv,
+ const char *const buf, const size_t buf_len,
+ const char **const pp,
+ ValuesStack *const stack,
+ ContainerStack *const container_stack,
+ bool *const next_map_special,
+ bool *const didcomma,
+ bool *const didcolon)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE
+{
+ const char *const e = buf + buf_len;
+ const char *p = *pp;
+ size_t len = 0;
+ const char *const s = ++p;
+ int ret = OK;
+ while (p < e && *p != '"') {
+ if (*p == '\\') {
+ p++;
+ if (p == e) {
+ emsgf(_("E474: Unfinished escape sequence: %.*s"),
+ (int) buf_len, buf);
+ goto parse_json_string_fail;
+ }
+ switch (*p) {
+ case 'u': {
+ if (p + 4 >= e) {
+ emsgf(_("E474: Unfinished unicode escape sequence: %.*s"),
+ (int) buf_len, buf);
+ goto parse_json_string_fail;
+ } else if (!ascii_isxdigit(p[1])
+ || !ascii_isxdigit(p[2])
+ || !ascii_isxdigit(p[3])
+ || !ascii_isxdigit(p[4])) {
+ emsgf(_("E474: Expected four hex digits after \\u: %.*s"),
+ LENP(p - 1, e));
+ goto parse_json_string_fail;
+ }
+ // One UTF-8 character below U+10000 can take up to 3 bytes,
+ // above up to 6, but they are encoded using two \u escapes.
+ len += 3;
+ p += 5;
+ break;
+ }
+ case '\\':
+ case '/':
+ case '"':
+ case 't':
+ case 'b':
+ case 'n':
+ case 'r':
+ case 'f': {
+ len++;
+ p++;
+ break;
+ }
+ default: {
+ emsgf(_("E474: Unknown escape sequence: %.*s"), LENP(p - 1, e));
+ goto parse_json_string_fail;
+ }
+ }
+ } else {
+ uint8_t p_byte = (uint8_t) *p;
+ // unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
+ if (p_byte < 0x20) {
+ emsgf(_("E474: ASCII control characters cannot be present "
+ "inside string: %.*s"), LENP(p, e));
+ goto parse_json_string_fail;
+ }
+ const int ch = utf_ptr2char((char_u *) p);
+ // All characters above U+007F are encoded using two or more bytes
+ // and thus cannot possibly be equal to *p. But utf_ptr2char({0xFF,
+ // 0}) will return 0xFF, even though 0xFF cannot start any UTF-8
+ // code point at all.
+ //
+ // The only exception is U+00C3 which is represented as 0xC3 0x83.
+ if (ch >= 0x80 && p_byte == ch
+ && !(ch == 0xC3 && p + 1 < e && (uint8_t) p[1] == 0x83)) {
+ emsgf(_("E474: Only UTF-8 strings allowed: %.*s"), LENP(p, e));
+ goto parse_json_string_fail;
+ } else if (ch > 0x10FFFF) {
+ emsgf(_("E474: Only UTF-8 code points up to U+10FFFF "
+ "are allowed to appear unescaped: %.*s"), LENP(p, e));
+ goto parse_json_string_fail;
+ }
+ const size_t ch_len = (size_t) utf_char2len(ch);
+ assert(ch_len == (size_t) (ch ? utf_ptr2len((char_u *) p) : 1));
+ len += ch_len;
+ p += ch_len;
+ }
+ }
+ if (p == e || *p != '"') {
+ emsgf(_("E474: Expected string end: %.*s"), (int) buf_len, buf);
+ goto parse_json_string_fail;
+ }
+ if (len == 0) {
+ POP(((typval_T) {
+ .v_type = VAR_STRING,
+ .vval = { .v_string = NULL },
+ }), false);
+ goto parse_json_string_ret;
+ }
+ char *str = xmalloc(len + 1);
+ int fst_in_pair = 0;
+ char *str_end = str;
+ bool hasnul = false;
+#define PUT_FST_IN_PAIR(fst_in_pair, str_end) \
+ do { \
+ if (fst_in_pair != 0) { \
+ str_end += utf_char2bytes(fst_in_pair, (char_u *) str_end); \
+ fst_in_pair = 0; \
+ } \
+ } while (0)
+ for (const char *t = s; t < p; t++) {
+ if (t[0] != '\\' || t[1] != 'u') {
+ PUT_FST_IN_PAIR(fst_in_pair, str_end);
+ }
+ if (*t == '\\') {
+ t++;
+ switch (*t) {
+ case 'u': {
+ const char ubuf[] = { t[1], t[2], t[3], t[4] };
+ t += 4;
+ unsigned long ch;
+ vim_str2nr((char_u *) ubuf, NULL, NULL,
+ STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4);
+ if (ch == 0) {
+ hasnul = true;
+ }
+ if (SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END) {
+ PUT_FST_IN_PAIR(fst_in_pair, str_end);
+ fst_in_pair = (int) ch;
+ } else if (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END
+ && fst_in_pair != 0) {
+ const int full_char = (
+ (int) (ch - SURROGATE_LO_START)
+ + ((fst_in_pair - SURROGATE_HI_START) << 10)
+ + SURROGATE_FIRST_CHAR);
+ str_end += utf_char2bytes(full_char, (char_u *) str_end);
+ fst_in_pair = 0;
+ } else {
+ PUT_FST_IN_PAIR(fst_in_pair, str_end);
+ str_end += utf_char2bytes((int) ch, (char_u *) str_end);
+ }
+ break;
+ }
+ case '\\':
+ case '/':
+ case '"':
+ case 't':
+ case 'b':
+ case 'n':
+ case 'r':
+ case 'f': {
+ static const char escapes[] = {
+ ['\\'] = '\\',
+ ['/'] = '/',
+ ['"'] = '"',
+ ['t'] = TAB,
+ ['b'] = BS,
+ ['n'] = NL,
+ ['r'] = CAR,
+ ['f'] = FF,
+ };
+ *str_end++ = escapes[(int) *t];
+ break;
+ }
+ default: {
+ assert(false);
+ }
+ }
+ } else {
+ *str_end++ = *t;
+ }
+ }
+ PUT_FST_IN_PAIR(fst_in_pair, str_end);
+#undef PUT_FST_IN_PAIR
+ if (conv->vc_type != CONV_NONE) {
+ size_t str_len = (size_t) (str_end - str);
+ char *const new_str = (char *) string_convert(conv, (char_u *) str,
+ &str_len);
+ if (new_str == NULL) {
+ emsgf(_("E474: Failed to convert string \"%.*s\" from UTF-8"),
+ (int) str_len, str);
+ xfree(str);
+ goto parse_json_string_fail;
+ }
+ xfree(str);
+ str = new_str;
+ str_end = new_str + str_len;
+ }
+ if (hasnul) {
+ typval_T obj;
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(&obj, kMPString, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ if (encode_list_write((void *) list, str, (size_t) (str_end - str))
+ == -1) {
+ clear_tv(&obj);
+ goto parse_json_string_fail;
+ }
+ xfree(str);
+ POP(obj, true);
+ } else {
+ *str_end = NUL;
+ POP(((typval_T) {
+ .v_type = VAR_STRING,
+ .vval = { .v_string = (char_u *) str },
+ }), false);
+ }
+ goto parse_json_string_ret;
+parse_json_string_fail:
+ ret = FAIL;
+parse_json_string_ret:
+ *pp = p;
+ return ret;
+}
+
+#undef POP
+
+/// Parse JSON number: both floating-point and integer
+///
+/// Number format: `-?\d+(?:.\d+)?(?:[eE][+-]?\d+)?`.
+///
+/// @param[in] buf Buffer being converted.
+/// @param[in] buf_len Length of the buffer.
+/// @param[in,out] pp Pointer to the start of the number. Must point to
+/// a digit or a minus sign. Is advanced to the last
+/// character of the number. Also see json_decoder_pop(), it
+/// may set pp to another location and alter
+/// next_map_special, didcomma and didcolon.
+/// @param[out] stack Object stack.
+/// @param[out] container_stack Container objects stack.
+/// @param[out] next_map_special Is set to true when dictionary is converted
+/// to a special map, otherwise not touched.
+/// @param[out] didcomma True if previous token was comma. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+/// @param[out] didcolon True if previous token was colon. Is set to recorded
+/// value when decoder is restarted, otherwise unused.
+///
+/// @return OK in case of success, FAIL in case of error.
+static inline int parse_json_number(const char *const buf, const size_t buf_len,
+ const char **const pp,
+ ValuesStack *const stack,
+ ContainerStack *const container_stack,
+ bool *const next_map_special,
+ bool *const didcomma,
+ bool *const didcolon)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE
+{
+ const char *const e = buf + buf_len;
+ const char *p = *pp;
+ int ret = OK;
+ const char *const s = p;
+ const char *ints = NULL;
+ const char *fracs = NULL;
+ const char *exps = NULL;
+ const char *exps_s = NULL;
+ if (*p == '-') {
+ p++;
+ }
+ ints = p;
+ if (p >= e) {
+ goto parse_json_number_check;
+ }
+ while (p < e && ascii_isdigit(*p)) {
+ p++;
+ }
+ if (p != ints + 1 && *ints == '0') {
+ emsgf(_("E474: Leading zeroes are not allowed: %.*s"), LENP(s, e));
+ goto parse_json_number_fail;
+ }
+ if (p >= e || p == ints) {
+ goto parse_json_number_check;
+ }
+ if (*p == '.') {
+ p++;
+ fracs = p;
+ while (p < e && ascii_isdigit(*p)) {
+ p++;
+ }
+ if (p >= e || p == fracs) {
+ goto parse_json_number_check;
+ }
+ }
+ if (*p == 'e' || *p == 'E') {
+ p++;
+ exps_s = p;
+ if (p < e && (*p == '-' || *p == '+')) {
+ p++;
+ }
+ exps = p;
+ while (p < e && ascii_isdigit(*p)) {
+ p++;
+ }
+ }
+parse_json_number_check:
+ if (p == ints) {
+ emsgf(_("E474: Missing number after minus sign: %.*s"), LENP(s, e));
+ goto parse_json_number_fail;
+ } else if (p == fracs || exps_s == fracs + 1) {
+ emsgf(_("E474: Missing number after decimal dot: %.*s"), LENP(s, e));
+ goto parse_json_number_fail;
+ } else if (p == exps) {
+ emsgf(_("E474: Missing exponent: %.*s"), LENP(s, e));
+ goto parse_json_number_fail;
+ }
+ typval_T tv = {
+ .v_type = VAR_NUMBER,
+ .v_lock = VAR_UNLOCKED,
+ };
+ const size_t exp_num_len = (size_t) (p - s);
+ if (fracs || exps) {
+ // Convert floating-point number
+ const size_t num_len = string2float(s, &tv.vval.v_float);
+ if (exp_num_len != num_len) {
+ emsgf(_("E685: internal error: while converting number \"%.*s\" "
+ "to float string2float consumed %zu bytes in place of %zu"),
+ (int) exp_num_len, s, num_len, exp_num_len);
+ }
+ tv.v_type = VAR_FLOAT;
+ } else {
+ // Convert integer
+ long nr;
+ int num_len;
+ vim_str2nr((char_u *) s, NULL, &num_len, 0, &nr, NULL, (int) (p - s));
+ if ((int) exp_num_len != num_len) {
+ emsgf(_("E685: internal error: while converting number \"%.*s\" "
+ "to integer vim_str2nr consumed %i bytes in place of %zu"),
+ (int) exp_num_len, s, num_len, exp_num_len);
+ }
+ tv.vval.v_number = (varnumber_T) nr;
+ }
+ if (json_decoder_pop(OBJ(tv, false, *didcomma, *didcolon),
+ stack, container_stack,
+ &p, next_map_special, didcomma, didcolon) == FAIL) {
+ goto parse_json_number_fail;
+ }
+ if (*next_map_special) {
+ goto parse_json_number_ret;
+ }
+ p--;
+ goto parse_json_number_ret;
+parse_json_number_fail:
+ ret = FAIL;
+parse_json_number_ret:
+ *pp = p;
+ return ret;
+}
+
+#define POP(obj_tv, is_sp_string) \
+ do { \
+ if (json_decoder_pop(OBJ(obj_tv, is_sp_string, didcomma, didcolon), \
+ &stack, &container_stack, \
+ &p, &next_map_special, &didcomma, &didcolon) \
+ == FAIL) { \
+ goto json_decode_string_fail; \
+ } \
+ if (next_map_special) { \
+ goto json_decode_string_cycle_start; \
+ } \
+ } while (0)
+
+/// Convert JSON string into VimL object
+///
+/// @param[in] buf String to convert. UTF-8 encoding is assumed.
+/// @param[in] buf_len Length of the string.
+/// @param[out] rettv Location where to save results.
+///
+/// @return OK in case of success, FAIL otherwise.
+int json_decode_string(const char *const buf, const size_t buf_len,
+ typval_T *const rettv)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ const char *p = buf;
+ const char *const e = buf + buf_len;
+ while (p < e && (*p == ' ' || *p == TAB || *p == NL || *p == CAR)) {
+ p++;
+ }
+ if (p == e) {
+ EMSG(_("E474: Attempt to decode a blank string"));
+ return FAIL;
+ }
+ vimconv_T conv = { .vc_type = CONV_NONE };
+ convert_setup(&conv, (char_u *) "utf-8", p_enc);
+ conv.vc_fail = true;
+ int ret = OK;
+ ValuesStack stack;
+ kv_init(stack);
+ ContainerStack container_stack;
+ kv_init(container_stack);
+ rettv->v_type = VAR_UNKNOWN;
+ bool didcomma = false;
+ bool didcolon = false;
+ bool next_map_special = false;
+ for (; p < e; p++) {
+json_decode_string_cycle_start:
+ assert(*p == '{' || next_map_special == false);
+ switch (*p) {
+ case '}':
+ case ']': {
+ if (kv_size(container_stack) == 0) {
+ emsgf(_("E474: No container to close: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ ContainerStackItem last_container = kv_last(container_stack);
+ if (*p == '}' && last_container.container.v_type != VAR_DICT) {
+ emsgf(_("E474: Closing list with curly bracket: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (*p == ']' && last_container.container.v_type != VAR_LIST) {
+ emsgf(_("E474: Closing dictionary with square bracket: %.*s"),
+ LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (didcomma) {
+ emsgf(_("E474: Trailing comma: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (didcolon) {
+ emsgf(_("E474: Expected value after colon: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (last_container.stack_index != kv_size(stack) - 1) {
+ assert(last_container.stack_index < kv_size(stack) - 1);
+ emsgf(_("E474: Expected value: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ if (kv_size(stack) == 1) {
+ p++;
+ (void) kv_pop(container_stack);
+ goto json_decode_string_after_cycle;
+ } else {
+ if (json_decoder_pop(kv_pop(stack), &stack, &container_stack, &p,
+ &next_map_special, &didcomma, &didcolon)
+ == FAIL) {
+ goto json_decode_string_fail;
+ }
+ assert(!next_map_special);
+ break;
+ }
+ }
+ case ',': {
+ if (kv_size(container_stack) == 0) {
+ emsgf(_("E474: Comma not inside container: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ ContainerStackItem last_container = kv_last(container_stack);
+ if (didcomma) {
+ emsgf(_("E474: Duplicate comma: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (didcolon) {
+ emsgf(_("E474: Comma after colon: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (last_container.container.v_type == VAR_DICT
+ && last_container.stack_index != kv_size(stack) - 1) {
+ emsgf(_("E474: Using comma in place of colon: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (last_container.special_val == NULL
+ ? (last_container.container.v_type == VAR_DICT
+ ? (DICT_LEN(last_container.container.vval.v_dict) == 0)
+ : (last_container.container.vval.v_list->lv_len == 0))
+ : (last_container.special_val->lv_len == 0)) {
+ emsgf(_("E474: Leading comma: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ didcomma = true;
+ continue;
+ }
+ case ':': {
+ if (kv_size(container_stack) == 0) {
+ emsgf(_("E474: Colon not inside container: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ ContainerStackItem last_container = kv_last(container_stack);
+ if (last_container.container.v_type != VAR_DICT) {
+ emsgf(_("E474: Using colon not in dictionary: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (last_container.stack_index != kv_size(stack) - 2) {
+ emsgf(_("E474: Unexpected colon: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (didcomma) {
+ emsgf(_("E474: Colon after comma: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ } else if (didcolon) {
+ emsgf(_("E474: Duplicate colon: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ didcolon = true;
+ continue;
+ }
+ case ' ':
+ case TAB:
+ case NL:
+ case CAR: {
+ continue;
+ }
+ case 'n': {
+ if ((p + 3) >= e || strncmp(p + 1, "ull", 3) != 0) {
+ emsgf(_("E474: Expected null: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ p += 3;
+ POP(((typval_T) {
+ .v_type = VAR_SPECIAL,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_special = kSpecialVarNull },
+ }), false);
+ break;
+ }
+ case 't': {
+ if ((p + 3) >= e || strncmp(p + 1, "rue", 3) != 0) {
+ emsgf(_("E474: Expected true: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ p += 3;
+ POP(((typval_T) {
+ .v_type = VAR_SPECIAL,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_special = kSpecialVarTrue },
+ }), false);
+ break;
+ }
+ case 'f': {
+ if ((p + 4) >= e || strncmp(p + 1, "alse", 4) != 0) {
+ emsgf(_("E474: Expected false: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ p += 4;
+ POP(((typval_T) {
+ .v_type = VAR_SPECIAL,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_special = kSpecialVarFalse },
+ }), false);
+ break;
+ }
+ case '"': {
+ if (parse_json_string(&conv, buf, buf_len, &p, &stack, &container_stack,
+ &next_map_special, &didcomma, &didcolon)
+ == FAIL) {
+ // Error message was already given
+ goto json_decode_string_fail;
+ }
+ if (next_map_special) {
+ goto json_decode_string_cycle_start;
+ }
+ break;
+ }
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ if (parse_json_number(buf, buf_len, &p, &stack, &container_stack,
+ &next_map_special, &didcomma, &didcolon)
+ == FAIL) {
+ // Error message was already given
+ goto json_decode_string_fail;
+ }
+ if (next_map_special) {
+ goto json_decode_string_cycle_start;
+ }
+ break;
+ }
+ case '[': {
+ list_T *list = list_alloc();
+ list->lv_refcount++;
+ typval_T tv = {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ };
+ kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) {
+ .stack_index = kv_size(stack),
+ .s = p,
+ .container = tv,
+ .special_val = NULL,
+ }));
+ kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon));
+ break;
+ }
+ case '{': {
+ typval_T tv;
+ list_T *val_list = NULL;
+ if (next_map_special) {
+ next_map_special = false;
+ val_list = list_alloc();
+ val_list->lv_refcount++;
+ create_special_dict(&tv, kMPMap, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = val_list },
+ }));
+ } else {
+ dict_T *dict = dict_alloc();
+ dict->dv_refcount++;
+ tv = (typval_T) {
+ .v_type = VAR_DICT,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_dict = dict },
+ };
+ }
+ kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) {
+ .stack_index = kv_size(stack),
+ .s = p,
+ .container = tv,
+ .special_val = val_list,
+ }));
+ kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon));
+ break;
+ }
+ default: {
+ emsgf(_("E474: Unidentified byte: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ }
+ didcomma = false;
+ didcolon = false;
+ if (kv_size(container_stack) == 0) {
+ p++;
+ break;
+ }
+ }
+json_decode_string_after_cycle:
+ for (; p < e; p++) {
+ switch (*p) {
+ case NL:
+ case ' ':
+ case TAB:
+ case CAR: {
+ break;
+ }
+ default: {
+ emsgf(_("E474: Trailing characters: %.*s"), LENP(p, e));
+ goto json_decode_string_fail;
+ }
+ }
+ }
+ if (kv_size(stack) == 1 && kv_size(container_stack) == 0) {
+ *rettv = kv_pop(stack).val;
+ goto json_decode_string_ret;
+ }
+ emsgf(_("E474: Unexpected end of input: %.*s"), (int) buf_len, buf);
+json_decode_string_fail:
+ ret = FAIL;
+ while (kv_size(stack)) {
+ clear_tv(&(kv_pop(stack).val));
+ }
+json_decode_string_ret:
+ kv_destroy(stack);
+ kv_destroy(container_stack);
+ return ret;
+}
+
+#undef LENP
+#undef POP
+
+#undef OBJ
+
+#undef DICT_LEN
+
+/// Convert msgpack object to a VimL one
+int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ switch (mobj.type) {
+ case MSGPACK_OBJECT_NIL: {
+ *rettv = (typval_T) {
+ .v_type = VAR_SPECIAL,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_special = kSpecialVarNull },
+ };
+ break;
+ }
+ case MSGPACK_OBJECT_BOOLEAN: {
+ *rettv = (typval_T) {
+ .v_type = VAR_SPECIAL,
+ .v_lock = VAR_UNLOCKED,
+ .vval = {
+ .v_special = mobj.via.boolean ? kSpecialVarTrue : kSpecialVarFalse
+ },
+ };
+ break;
+ }
+ case MSGPACK_OBJECT_POSITIVE_INTEGER: {
+ if (mobj.via.u64 <= VARNUMBER_MAX) {
+ *rettv = (typval_T) {
+ .v_type = VAR_NUMBER,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_number = (varnumber_T) mobj.via.u64 },
+ };
+ } else {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(rettv, kMPInteger, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ uint64_t n = mobj.via.u64;
+ list_append_number(list, 1);
+ list_append_number(list, (varnumber_T) ((n >> 62) & 0x3));
+ list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF));
+ list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF));
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER: {
+ if (mobj.via.i64 >= VARNUMBER_MIN) {
+ *rettv = (typval_T) {
+ .v_type = VAR_NUMBER,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_number = (varnumber_T) mobj.via.i64 },
+ };
+ } else {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(rettv, kMPInteger, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ uint64_t n = -((uint64_t) mobj.via.i64);
+ list_append_number(list, -1);
+ list_append_number(list, (varnumber_T) ((n >> 62) & 0x3));
+ list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF));
+ list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF));
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_FLOAT: {
+ *rettv = (typval_T) {
+ .v_type = VAR_FLOAT,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_float = mobj.via.f64 },
+ };
+ break;
+ }
+ case MSGPACK_OBJECT_STR: {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(rettv, kMPString, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ if (encode_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size)
+ == -1) {
+ return FAIL;
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_BIN: {
+ if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) {
+ *rettv = (typval_T) {
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) },
+ };
+ break;
+ }
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(rettv, kMPBinary, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ if (encode_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size)
+ == -1) {
+ return FAIL;
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_ARRAY: {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ *rettv = (typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ };
+ for (size_t i = 0; i < mobj.via.array.size; i++) {
+ listitem_T *const li = listitem_alloc();
+ li->li_tv.v_type = VAR_UNKNOWN;
+ list_append(list, li);
+ if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) {
+ return FAIL;
+ }
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_MAP: {
+ for (size_t i = 0; i < mobj.via.map.size; i++) {
+ if (mobj.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR
+ || mobj.via.map.ptr[i].key.via.str.size == 0
+ || memchr(mobj.via.map.ptr[i].key.via.str.ptr, NUL,
+ mobj.via.map.ptr[i].key.via.str.size) != NULL) {
+ goto msgpack_to_vim_generic_map;
+ }
+ }
+ dict_T *const dict = dict_alloc();
+ dict->dv_refcount++;
+ *rettv = (typval_T) {
+ .v_type = VAR_DICT,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_dict = dict },
+ };
+ for (size_t i = 0; i < mobj.via.map.size; i++) {
+ dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key)
+ + mobj.via.map.ptr[i].key.via.str.size);
+ memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr,
+ mobj.via.map.ptr[i].key.via.str.size);
+ di->di_tv.v_type = VAR_UNKNOWN;
+ if (dict_add(dict, di) == FAIL) {
+ // Duplicate key: fallback to generic map
+ clear_tv(rettv);
+ xfree(di);
+ goto msgpack_to_vim_generic_map;
+ }
+ if (msgpack_to_vim(mobj.via.map.ptr[i].val, &di->di_tv) == FAIL) {
+ return FAIL;
+ }
+ }
+ break;
+msgpack_to_vim_generic_map: {}
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(rettv, kMPMap, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ for (size_t i = 0; i < mobj.via.map.size; i++) {
+ list_T *const kv_pair = list_alloc();
+ list_append_list(list, kv_pair);
+ listitem_T *const key_li = listitem_alloc();
+ key_li->li_tv.v_type = VAR_UNKNOWN;
+ list_append(kv_pair, key_li);
+ listitem_T *const val_li = listitem_alloc();
+ val_li->li_tv.v_type = VAR_UNKNOWN;
+ list_append(kv_pair, val_li);
+ if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) {
+ return FAIL;
+ }
+ if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) {
+ return FAIL;
+ }
+ }
+ break;
+ }
+ case MSGPACK_OBJECT_EXT: {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ list_append_number(list, mobj.via.ext.type);
+ list_T *const ext_val_list = list_alloc();
+ list_append_list(list, ext_val_list);
+ create_special_dict(rettv, kMPExt, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ if (encode_list_write((void *) ext_val_list, mobj.via.ext.ptr,
+ mobj.via.ext.size) == -1) {
+ return FAIL;
+ }
+ break;
+ }
+ }
+ return OK;
+}
diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h
new file mode 100644
index 0000000000..5c25a64f7a
--- /dev/null
+++ b/src/nvim/eval/decode.h
@@ -0,0 +1,13 @@
+#ifndef NVIM_EVAL_DECODE_H
+#define NVIM_EVAL_DECODE_H
+
+#include <stddef.h>
+
+#include <msgpack.h>
+
+#include "nvim/eval_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/decode.h.generated.h"
+#endif
+#endif // NVIM_EVAL_DECODE_H
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
new file mode 100644
index 0000000000..c651a50be9
--- /dev/null
+++ b/src/nvim/eval/encode.c
@@ -0,0 +1,1296 @@
+/// @file encode.c
+///
+/// File containing functions for encoding and decoding VimL values.
+///
+/// Split out from eval.c.
+
+#include <msgpack.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <math.h>
+
+#include "nvim/eval/encode.h"
+#include "nvim/buffer_defs.h" // vimconv_T
+#include "nvim/eval.h"
+#include "nvim/eval_defs.h"
+#include "nvim/garray.h"
+#include "nvim/mbyte.h"
+#include "nvim/message.h"
+#include "nvim/memory.h"
+#include "nvim/charset.h" // vim_isprintc()
+#include "nvim/macros.h"
+#include "nvim/ascii.h"
+#include "nvim/vim.h" // For _()
+#include "nvim/lib/kvec.h"
+
+#define ga_concat(a, b) ga_concat(a, (char_u *)b)
+#define utf_ptr2char(b) utf_ptr2char((char_u *)b)
+#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b))
+#define utf_char2len(b) ((size_t)utf_char2len(b))
+#define string_convert(a, b, c) \
+ ((char *)string_convert((vimconv_T *)a, (char_u *)b, c))
+#define convert_setup(vcp, from, to) \
+ (convert_setup(vcp, (char_u *)from, (char_u *)to))
+
+/// Structure representing current VimL to messagepack conversion state
+typedef struct {
+ enum {
+ kMPConvDict, ///< Convert dict_T *dictionary.
+ kMPConvList, ///< Convert list_T *list.
+ kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
+ } type;
+ union {
+ struct {
+ dict_T *dict; ///< Currently converted dictionary.
+ hashitem_T *hi; ///< Currently converted dictionary item.
+ size_t todo; ///< Amount of items left to process.
+ } d; ///< State of dictionary conversion.
+ struct {
+ list_T *list; ///< Currently converted list.
+ listitem_T *li; ///< Currently converted list item.
+ } l; ///< State of list or generic mapping conversion.
+ } data; ///< Data to convert.
+} MPConvStackVal;
+
+/// Stack used to convert VimL values to messagepack.
+typedef kvec_t(MPConvStackVal) MPConvStack;
+
+const char *const encode_special_var_names[] = {
+ [kSpecialVarNull] = "null",
+ [kSpecialVarTrue] = "true",
+ [kSpecialVarFalse] = "false",
+};
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/encode.c.generated.h"
+#endif
+
+/// Msgpack callback for writing to readfile()-style list
+int encode_list_write(void *data, const char *buf, size_t len)
+{
+ if (len == 0) {
+ return 0;
+ }
+ list_T *const list = (list_T *) data;
+ const char *const end = buf + len;
+ const char *line_end = buf;
+ listitem_T *li = list->lv_last;
+
+ // Continue the last list element
+ if (li != NULL) {
+ line_end = xmemscan(buf, NL, len);
+ if (line_end != buf) {
+ const size_t line_length = (size_t)(line_end - buf);
+ char *str = (char *)li->li_tv.vval.v_string;
+ const size_t li_len = (str == NULL ? 0 : strlen(str));
+ li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1);
+ str = (char *)li->li_tv.vval.v_string + li_len;
+ memcpy(str, buf, line_length);
+ str[line_length] = 0;
+ memchrsub(str, NUL, NL, line_length);
+ }
+ line_end++;
+ }
+
+ while (line_end < end) {
+ const char *line_start = line_end;
+ line_end = xmemscan(line_start, NL, (size_t) (end - line_start));
+ char *str = NULL;
+ if (line_end != line_start) {
+ const size_t line_length = (size_t)(line_end - line_start);
+ str = xmemdupz(line_start, line_length);
+ memchrsub(str, NUL, NL, line_length);
+ }
+ list_append_allocated_string(list, str);
+ line_end++;
+ }
+ if (line_end == end) {
+ list_append_allocated_string(list, NULL);
+ }
+ return 0;
+}
+
+/// Abort conversion to string after a recursion error.
+static bool did_echo_string_emsg = false;
+
+/// Show a error message when converting to msgpack value
+///
+/// @param[in] msg Error message to dump. Must contain exactly two %s that
+/// will be replaced with what was being dumped: first with
+/// something like “F†or “function argumentâ€, second with path
+/// to the failed value.
+/// @param[in] mpstack Path to the failed value.
+/// @param[in] objname Dumped object name.
+///
+/// @return FAIL.
+static int conv_error(const char *const msg, const MPConvStack *const mpstack,
+ const char *const objname)
+ FUNC_ATTR_NONNULL_ALL
+{
+ garray_T msg_ga;
+ ga_init(&msg_ga, (int)sizeof(char), 80);
+ char *const key_msg = _("key %s");
+ char *const key_pair_msg = _("key %s at index %i from special map");
+ char *const idx_msg = _("index %i");
+ for (size_t i = 0; i < kv_size(*mpstack); i++) {
+ if (i != 0) {
+ ga_concat(&msg_ga, ", ");
+ }
+ MPConvStackVal v = kv_A(*mpstack, i);
+ switch (v.type) {
+ case kMPConvDict: {
+ typval_T key_tv = {
+ .v_type = VAR_STRING,
+ .vval = { .v_string = (v.data.d.hi == NULL
+ ? v.data.d.dict->dv_hashtab.ht_array
+ : (v.data.d.hi - 1))->hi_key },
+ };
+ char *const key = encode_tv2string(&key_tv, NULL);
+ vim_snprintf((char *) IObuff, IOSIZE, key_msg, key);
+ xfree(key);
+ ga_concat(&msg_ga, IObuff);
+ break;
+ }
+ case kMPConvPairs:
+ case kMPConvList: {
+ int idx = 0;
+ const listitem_T *li;
+ for (li = v.data.l.list->lv_first;
+ li != NULL && li->li_next != v.data.l.li;
+ li = li->li_next) {
+ idx++;
+ }
+ if (v.type == kMPConvList
+ || li == NULL
+ || (li->li_tv.v_type != VAR_LIST
+ && li->li_tv.vval.v_list->lv_len <= 0)) {
+ vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx);
+ ga_concat(&msg_ga, IObuff);
+ } else {
+ typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv;
+ char *const key = encode_tv2echo(&key_tv, NULL);
+ vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx);
+ xfree(key);
+ ga_concat(&msg_ga, IObuff);
+ }
+ break;
+ }
+ }
+ }
+ EMSG3(msg, objname, (kv_size(*mpstack) == 0
+ ? _("itself")
+ : (char *) msg_ga.ga_data));
+ ga_clear(&msg_ga);
+ return FAIL;
+}
+
+/// Convert readfile()-style list to a char * buffer with length
+///
+/// @param[in] list Converted list.
+/// @param[out] ret_len Resulting buffer length.
+/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is
+/// zero.
+///
+/// @return true in case of success, false in case of failure.
+bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len,
+ char **const ret_buf)
+ FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ size_t len = 0;
+ if (list != NULL) {
+ for (const listitem_T *li = list->lv_first;
+ li != NULL;
+ li = li->li_next) {
+ if (li->li_tv.v_type != VAR_STRING) {
+ return false;
+ }
+ len++;
+ if (li->li_tv.vval.v_string != 0) {
+ len += STRLEN(li->li_tv.vval.v_string);
+ }
+ }
+ if (len) {
+ len--;
+ }
+ }
+ *ret_len = len;
+ if (len == 0) {
+ *ret_buf = NULL;
+ return true;
+ }
+ ListReaderState lrstate = encode_init_lrstate(list);
+ char *const buf = xmalloc(len);
+ size_t read_bytes;
+ if (encode_read_from_list(&lrstate, buf, len, &read_bytes) != OK) {
+ assert(false);
+ }
+ assert(len == read_bytes);
+ *ret_buf = buf;
+ return true;
+}
+
+/// Read bytes from list
+///
+/// @param[in,out] state Structure describing position in list from which
+/// reading should start. Is updated to reflect position
+/// at which reading ended.
+/// @param[out] buf Buffer to write to.
+/// @param[in] nbuf Buffer length.
+/// @param[out] read_bytes Is set to amount of bytes read.
+///
+/// @return OK when reading was finished, FAIL in case of error (i.e. list item
+/// was not a string), NOTDONE if reading was successfull, but there are
+/// more bytes to read.
+int encode_read_from_list(ListReaderState *const state, char *const buf,
+ const size_t nbuf, size_t *const read_bytes)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ char *const buf_end = buf + nbuf;
+ char *p = buf;
+ while (p < buf_end) {
+ for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) {
+ const char ch = (char) state->li->li_tv.vval.v_string[state->offset++];
+ *p++ = (char) ((char) ch == (char) NL ? (char) NUL : (char) ch);
+ }
+ if (p < buf_end) {
+ state->li = state->li->li_next;
+ if (state->li == NULL) {
+ *read_bytes = (size_t) (p - buf);
+ return OK;
+ }
+ *p++ = NL;
+ if (state->li->li_tv.v_type != VAR_STRING) {
+ *read_bytes = (size_t) (p - buf);
+ return FAIL;
+ }
+ state->offset = 0;
+ state->li_length = (state->li->li_tv.vval.v_string == NULL
+ ? 0
+ : STRLEN(state->li->li_tv.vval.v_string));
+ }
+ }
+ *read_bytes = nbuf;
+ return (state->offset < state->li_length || state->li->li_next != NULL
+ ? NOTDONE
+ : OK);
+}
+
+/// Code for checking whether container references itself
+///
+/// @param[in,out] val Container to check.
+/// @param copyID_attr Name of the container attribute that holds copyID.
+/// After checking whether value of this attribute is
+/// copyID (variable) it is set to copyID.
+#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \
+ do { \
+ if ((val)->copyID_attr == copyID) { \
+ CONV_RECURSE((val), conv_type); \
+ } \
+ (val)->copyID_attr = copyID; \
+ } while (0)
+
+#define TV_STRLEN(tv) \
+ (tv->vval.v_string == NULL ? 0 : STRLEN(tv->vval.v_string))
+
+/// Define functions which convert VimL value to something else
+///
+/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const
+/// tv)` which returns OK or FAIL and helper functions.
+///
+/// @param firstargtype Type of the first argument. It will be used to return
+/// the results.
+/// @param firstargname Name of the first argument.
+/// @param name Name of the target converter.
+#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \
+static int name##_convert_one_value(firstargtype firstargname, \
+ MPConvStack *const mpstack, \
+ typval_T *const tv, \
+ const int copyID, \
+ const char *const objname) \
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
+{ \
+ switch (tv->v_type) { \
+ case VAR_STRING: { \
+ CONV_STRING(tv->vval.v_string, TV_STRLEN(tv)); \
+ break; \
+ } \
+ case VAR_NUMBER: { \
+ CONV_NUMBER(tv->vval.v_number); \
+ break; \
+ } \
+ case VAR_FLOAT: { \
+ CONV_FLOAT(tv->vval.v_float); \
+ break; \
+ } \
+ case VAR_FUNC: { \
+ CONV_FUNC(tv->vval.v_string); \
+ break; \
+ } \
+ case VAR_LIST: { \
+ if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
+ CONV_EMPTY_LIST(); \
+ break; \
+ } \
+ CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, kMPConvList); \
+ CONV_LIST_START(tv->vval.v_list); \
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
+ .type = kMPConvList, \
+ .data = { \
+ .l = { \
+ .list = tv->vval.v_list, \
+ .li = tv->vval.v_list->lv_first, \
+ }, \
+ }, \
+ })); \
+ break; \
+ } \
+ case VAR_SPECIAL: { \
+ switch (tv->vval.v_special) { \
+ case kSpecialVarNull: { \
+ CONV_NIL(); \
+ break; \
+ } \
+ case kSpecialVarTrue: \
+ case kSpecialVarFalse: { \
+ CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \
+ break; \
+ } \
+ } \
+ break; \
+ } \
+ case VAR_DICT: { \
+ if (tv->vval.v_dict == NULL \
+ || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \
+ CONV_EMPTY_DICT(); \
+ break; \
+ } \
+ const dictitem_T *type_di; \
+ const dictitem_T *val_di; \
+ if (CONV_ALLOW_SPECIAL \
+ && tv->vval.v_dict->dv_hashtab.ht_used == 2 \
+ && (type_di = dict_find((dict_T *) tv->vval.v_dict, \
+ (char_u *) "_TYPE", -1)) != NULL \
+ && type_di->di_tv.v_type == VAR_LIST \
+ && (val_di = dict_find((dict_T *) tv->vval.v_dict, \
+ (char_u *) "_VAL", -1)) != NULL) { \
+ size_t i; \
+ for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \
+ if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \
+ break; \
+ } \
+ } \
+ if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ switch ((MessagePackType) i) { \
+ case kMPNil: { \
+ CONV_NIL(); \
+ break; \
+ } \
+ case kMPBoolean: { \
+ if (val_di->di_tv.v_type != VAR_NUMBER) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ CONV_BOOL(val_di->di_tv.vval.v_number); \
+ break; \
+ } \
+ case kMPInteger: { \
+ const list_T *val_list; \
+ varnumber_T sign; \
+ varnumber_T highest_bits; \
+ varnumber_T high_bits; \
+ varnumber_T low_bits; \
+ /* List of 4 integers; first is signed (should be 1 or -1, but */ \
+ /* this is not checked), second is unsigned and have at most */ \
+ /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \
+ /* bits is not checked), other unsigned and have at most 31 */ \
+ /* non-zero bits (number of bits is not checked).*/ \
+ if (val_di->di_tv.v_type != VAR_LIST \
+ || (val_list = val_di->di_tv.vval.v_list) == NULL \
+ || val_list->lv_len != 4 \
+ || val_list->lv_first->li_tv.v_type != VAR_NUMBER \
+ || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \
+ || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \
+ || (highest_bits = \
+ val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \
+ || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \
+ || (high_bits = \
+ val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \
+ || val_list->lv_last->li_tv.v_type != VAR_NUMBER \
+ || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \
+ | (uint64_t) (((uint64_t) high_bits) << 31) \
+ | (uint64_t) low_bits); \
+ if (sign > 0) { \
+ CONV_UNSIGNED_NUMBER(number); \
+ } else { \
+ CONV_NUMBER(-number); \
+ } \
+ break; \
+ } \
+ case kMPFloat: { \
+ if (val_di->di_tv.v_type != VAR_FLOAT) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ CONV_FLOAT(val_di->di_tv.vval.v_float); \
+ break; \
+ } \
+ case kMPString: \
+ case kMPBinary: { \
+ const bool is_string = ((MessagePackType) i == kMPString); \
+ if (val_di->di_tv.v_type != VAR_LIST) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ size_t len; \
+ char *buf; \
+ if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \
+ &buf)) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ if (is_string) { \
+ CONV_STR_STRING(buf, len); \
+ } else { \
+ CONV_STRING(buf, len); \
+ } \
+ xfree(buf); \
+ break; \
+ } \
+ case kMPArray: { \
+ if (val_di->di_tv.v_type != VAR_LIST) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, \
+ kMPConvList); \
+ CONV_LIST_START(val_di->di_tv.vval.v_list); \
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
+ .type = kMPConvList, \
+ .data = { \
+ .l = { \
+ .list = val_di->di_tv.vval.v_list, \
+ .li = val_di->di_tv.vval.v_list->lv_first, \
+ }, \
+ }, \
+ })); \
+ break; \
+ } \
+ case kMPMap: { \
+ if (val_di->di_tv.v_type != VAR_LIST) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ list_T *const val_list = val_di->di_tv.vval.v_list; \
+ if (val_list == NULL || val_list->lv_len == 0) { \
+ CONV_EMPTY_DICT(); \
+ break; \
+ } \
+ for (const listitem_T *li = val_list->lv_first; li != NULL; \
+ li = li->li_next) { \
+ if (li->li_tv.v_type != VAR_LIST \
+ || li->li_tv.vval.v_list->lv_len != 2) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ } \
+ CHECK_SELF_REFERENCE(val_list, lv_copyID, kMPConvPairs); \
+ CONV_DICT_START(val_list->lv_len); \
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
+ .type = kMPConvPairs, \
+ .data = { \
+ .l = { \
+ .list = val_list, \
+ .li = val_list->lv_first, \
+ }, \
+ }, \
+ })); \
+ break; \
+ } \
+ case kMPExt: { \
+ const list_T *val_list; \
+ varnumber_T type; \
+ if (val_di->di_tv.v_type != VAR_LIST \
+ || (val_list = val_di->di_tv.vval.v_list) == NULL \
+ || val_list->lv_len != 2 \
+ || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \
+ || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \
+ || type < INT8_MIN \
+ || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ size_t len; \
+ char *buf; \
+ if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \
+ &len, &buf)) { \
+ goto name##_convert_one_value_regular_dict; \
+ } \
+ CONV_EXT_STRING(buf, len, type); \
+ xfree(buf); \
+ break; \
+ } \
+ } \
+ break; \
+ } \
+name##_convert_one_value_regular_dict: \
+ CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, kMPConvDict); \
+ CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \
+ kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \
+ .type = kMPConvDict, \
+ .data = { \
+ .d = { \
+ .dict = tv->vval.v_dict, \
+ .hi = tv->vval.v_dict->dv_hashtab.ht_array, \
+ .todo = tv->vval.v_dict->dv_hashtab.ht_used, \
+ }, \
+ }, \
+ })); \
+ break; \
+ } \
+ case VAR_UNKNOWN: { \
+ EMSG2(_(e_intern2), #name "_convert_one_value()"); \
+ return FAIL; \
+ } \
+ } \
+ return OK; \
+} \
+\
+scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \
+ const char *const objname) \
+ FUNC_ATTR_WARN_UNUSED_RESULT \
+{ \
+ const int copyID = get_copyID(); \
+ MPConvStack mpstack; \
+ kv_init(mpstack); \
+ if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \
+ == FAIL) { \
+ goto encode_vim_to_##name##_error_ret; \
+ } \
+ while (kv_size(mpstack)) { \
+ MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \
+ typval_T *cur_tv = NULL; \
+ switch (cur_mpsv->type) { \
+ case kMPConvDict: { \
+ if (!cur_mpsv->data.d.todo) { \
+ (void) kv_pop(mpstack); \
+ cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \
+ CONV_DICT_END(); \
+ continue; \
+ } else if (cur_mpsv->data.d.todo \
+ != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \
+ CONV_DICT_BETWEEN_ITEMS(); \
+ } \
+ while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \
+ cur_mpsv->data.d.hi++; \
+ } \
+ dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \
+ cur_mpsv->data.d.todo--; \
+ cur_mpsv->data.d.hi++; \
+ CONV_STR_STRING(&di->di_key[0], STRLEN(&di->di_key[0])); \
+ CONV_DICT_AFTER_KEY(); \
+ cur_tv = &di->di_tv; \
+ break; \
+ } \
+ case kMPConvList: { \
+ if (cur_mpsv->data.l.li == NULL) { \
+ (void) kv_pop(mpstack); \
+ cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
+ CONV_LIST_END(cur_mpsv->data.l.list); \
+ continue; \
+ } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
+ CONV_LIST_BETWEEN_ITEMS(); \
+ } \
+ cur_tv = &cur_mpsv->data.l.li->li_tv; \
+ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
+ break; \
+ } \
+ case kMPConvPairs: { \
+ if (cur_mpsv->data.l.li == NULL) { \
+ (void) kv_pop(mpstack); \
+ cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
+ CONV_DICT_END(); \
+ continue; \
+ } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
+ CONV_DICT_BETWEEN_ITEMS(); \
+ } \
+ const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \
+ CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair); \
+ if (name##_convert_one_value(firstargname, &mpstack, \
+ &kv_pair->lv_first->li_tv, copyID, \
+ objname) == FAIL) { \
+ goto encode_vim_to_##name##_error_ret; \
+ } \
+ CONV_DICT_AFTER_KEY(); \
+ cur_tv = &kv_pair->lv_last->li_tv; \
+ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
+ break; \
+ } \
+ } \
+ assert(cur_tv != NULL); \
+ if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \
+ objname) == FAIL) { \
+ goto encode_vim_to_##name##_error_ret; \
+ } \
+ } \
+ kv_destroy(mpstack); \
+ return OK; \
+encode_vim_to_##name##_error_ret: \
+ kv_destroy(mpstack); \
+ return FAIL; \
+}
+
+#define CONV_STRING(buf, len) \
+ do { \
+ const char *const buf_ = (const char *) buf; \
+ if (buf == NULL) { \
+ ga_concat(gap, "''"); \
+ } else { \
+ const size_t len_ = (len); \
+ ga_grow(gap, (int) (2 + len_ + memcnt(buf_, '\'', len_))); \
+ ga_append(gap, '\''); \
+ for (size_t i = 0; i < len_; i++) { \
+ if (buf_[i] == '\'') { \
+ ga_append(gap, '\''); \
+ } \
+ ga_append(gap, buf_[i]); \
+ } \
+ ga_append(gap, '\''); \
+ } \
+ } while (0)
+
+#define CONV_STR_STRING(buf, len) \
+ CONV_STRING(buf, len)
+
+#define CONV_EXT_STRING(buf, len, type)
+
+#define CONV_NUMBER(num) \
+ do { \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \
+ ga_concat(gap, numbuf); \
+ } while (0)
+
+#define CONV_FLOAT(flt) \
+ do { \
+ const float_T flt_ = (flt); \
+ switch (fpclassify(flt_)) { \
+ case FP_NAN: { \
+ ga_concat(gap, (char_u *) "str2float('nan')"); \
+ break; \
+ } \
+ case FP_INFINITE: { \
+ if (flt_ < 0) { \
+ ga_append(gap, '-'); \
+ } \
+ ga_concat(gap, (char_u *) "str2float('inf')"); \
+ break; \
+ } \
+ default: { \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \
+ ga_concat(gap, (char_u *) numbuf); \
+ } \
+ } \
+ } while (0)
+
+#define CONV_FUNC(fun) \
+ do { \
+ ga_concat(gap, "function("); \
+ CONV_STRING(fun, STRLEN(fun)); \
+ ga_append(gap, ')'); \
+ } while (0)
+
+#define CONV_EMPTY_LIST() \
+ ga_concat(gap, "[]")
+
+#define CONV_LIST_START(lst) \
+ ga_append(gap, '[')
+
+#define CONV_EMPTY_DICT() \
+ ga_concat(gap, "{}")
+
+#define CONV_NIL() \
+ ga_concat(gap, "v:null")
+
+#define CONV_BOOL(num) \
+ ga_concat(gap, ((num)? "v:true": "v:false"))
+
+#define CONV_UNSIGNED_NUMBER(num)
+
+#define CONV_DICT_START(len) \
+ ga_append(gap, '{')
+
+#define CONV_DICT_END() \
+ ga_append(gap, '}')
+
+#define CONV_DICT_AFTER_KEY() \
+ ga_concat(gap, ": ")
+
+#define CONV_DICT_BETWEEN_ITEMS() \
+ ga_concat(gap, ", ")
+
+#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair)
+
+#define CONV_LIST_END(lst) \
+ ga_append(gap, ']')
+
+#define CONV_LIST_BETWEEN_ITEMS() \
+ CONV_DICT_BETWEEN_ITEMS()
+
+#define CONV_RECURSE(val, conv_type) \
+ do { \
+ if (!did_echo_string_emsg) { \
+ /* Only give this message once for a recursive call to avoid */ \
+ /* flooding the user with errors. */ \
+ did_echo_string_emsg = true; \
+ EMSG(_("E724: unable to correctly dump variable " \
+ "with self-referencing container")); \
+ } \
+ char ebuf[NUMBUFLEN + 7]; \
+ size_t backref = 0; \
+ for (; backref < kv_size(*mpstack); backref++) { \
+ const MPConvStackVal mpval = kv_A(*mpstack, backref); \
+ if (mpval.type == conv_type) { \
+ if (conv_type == kMPConvDict) { \
+ if ((void *) mpval.data.d.dict == (void *) (val)) { \
+ break; \
+ } \
+ } else if (conv_type == kMPConvList) { \
+ if ((void *) mpval.data.l.list == (void *) (val)) { \
+ break; \
+ } \
+ } \
+ } \
+ } \
+ vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{E724@%zu}", backref); \
+ ga_concat(gap, &ebuf[0]); \
+ return OK; \
+ } while (0)
+
+#define CONV_ALLOW_SPECIAL false
+
+DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap)
+
+#undef CONV_RECURSE
+#define CONV_RECURSE(val, conv_type) \
+ do { \
+ char ebuf[NUMBUFLEN + 7]; \
+ size_t backref = 0; \
+ for (; backref < kv_size(*mpstack); backref++) { \
+ const MPConvStackVal mpval = kv_A(*mpstack, backref); \
+ if (mpval.type == conv_type) { \
+ if (conv_type == kMPConvDict) { \
+ if ((void *) mpval.data.d.dict == (void *) val) { \
+ break; \
+ } \
+ } else if (conv_type == kMPConvList) { \
+ if ((void *) mpval.data.l.list == (void *) val) { \
+ break; \
+ } \
+ } \
+ } \
+ } \
+ if (conv_type == kMPConvDict) { \
+ vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{...@%zu}", backref); \
+ } else { \
+ vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "[...@%zu]", backref); \
+ } \
+ ga_concat(gap, &ebuf[0]); \
+ return OK; \
+ } while (0)
+
+DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap)
+
+#undef CONV_RECURSE
+#define CONV_RECURSE(val, conv_type) \
+ do { \
+ if (!did_echo_string_emsg) { \
+ /* Only give this message once for a recursive call to avoid */ \
+ /* flooding the user with errors. */ \
+ did_echo_string_emsg = true; \
+ EMSG(_("E724: unable to correctly dump variable " \
+ "with self-referencing container")); \
+ } \
+ return OK; \
+ } while (0)
+
+#undef CONV_ALLOW_SPECIAL
+#define CONV_ALLOW_SPECIAL true
+
+#undef CONV_NIL
+#define CONV_NIL() \
+ ga_concat(gap, "null")
+
+#undef CONV_BOOL
+#define CONV_BOOL(num) \
+ ga_concat(gap, ((num)? "true": "false"))
+
+#undef CONV_UNSIGNED_NUMBER
+#define CONV_UNSIGNED_NUMBER(num) \
+ do { \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \
+ ga_concat(gap, numbuf); \
+ } while (0)
+
+#undef CONV_FLOAT
+#define CONV_FLOAT(flt) \
+ do { \
+ const float_T flt_ = (flt); \
+ switch (fpclassify(flt_)) { \
+ case FP_NAN: { \
+ EMSG(_("E474: Unable to represent NaN value in JSON")); \
+ return FAIL; \
+ } \
+ case FP_INFINITE: { \
+ EMSG(_("E474: Unable to represent infinity in JSON")); \
+ return FAIL; \
+ } \
+ default: { \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \
+ ga_concat(gap, (char_u *) numbuf); \
+ break; \
+ } \
+ } \
+ } while (0)
+
+/// Last used p_enc value
+///
+/// Generic pointer: it is not used as a string, only pointer comparisons are
+/// performed. Must not be freed.
+static const void *last_p_enc = NULL;
+
+/// Conversion setup for converting from last_p_enc to UTF-8
+static vimconv_T p_enc_conv = {
+ .vc_type = CONV_NONE,
+};
+
+/// Escape sequences used in JSON
+static const char escapes[][3] = {
+ [BS] = "\\b",
+ [TAB] = "\\t",
+ [NL] = "\\n",
+ [CAR] = "\\r",
+ ['"'] = "\\\"",
+ ['\\'] = "\\\\",
+ [FF] = "\\f",
+};
+
+static const char xdigits[] = "0123456789ABCDEF";
+
+/// Convert given string to JSON string
+///
+/// @param[out] gap Garray where result will be saved.
+/// @param[in] buf Converted string.
+/// @param[in] len Converted string length.
+///
+/// @return OK in case of success, FAIL otherwise.
+static inline int convert_to_json_string(garray_T *const gap,
+ const char *const buf,
+ const size_t len)
+ FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_ALWAYS_INLINE
+{
+ const char *utf_buf = buf;
+ if (utf_buf == NULL) {
+ ga_concat(gap, "\"\"");
+ } else {
+ size_t utf_len = len;
+ char *tofree = NULL;
+ if (last_p_enc != (const void *) p_enc) {
+ p_enc_conv.vc_type = CONV_NONE;
+ convert_setup(&p_enc_conv, p_enc, "utf-8");
+ p_enc_conv.vc_fail = true;
+ last_p_enc = p_enc;
+ }
+ if (p_enc_conv.vc_type != CONV_NONE) {
+ tofree = string_convert(&p_enc_conv, buf, &utf_len);
+ if (tofree == NULL) {
+ emsgf(_("E474: Failed to convert string \"%.*s\" to UTF-8"),
+ utf_len, utf_buf);
+ return FAIL;
+ }
+ utf_buf = tofree;
+ }
+ size_t str_len = 0;
+ // Encode character as \u0000 if
+ // 1. It is an ASCII control character (0x0 .. 0x1F, 0x7F).
+ // 2. &encoding is not UTF-8 and code point is above 0x7F.
+ // 3. &encoding is UTF-8 and code point is not printable according to
+ // utf_printable().
+ // This is done to make it possible to :echo values when &encoding is not
+ // UTF-8.
+#define ENCODE_RAW(p_enc_conv, ch) \
+ (ch >= 0x20 && (p_enc_conv.vc_type == CONV_NONE \
+ ? utf_printable(ch) \
+ : ch < 0x7F))
+ for (size_t i = 0; i < utf_len;) {
+ const int ch = utf_ptr2char(utf_buf + i);
+ const size_t shift = (ch == 0? 1: utf_ptr2len(utf_buf + i));
+ assert(shift > 0);
+ i += shift;
+ switch (ch) {
+ case BS:
+ case TAB:
+ case NL:
+ case FF:
+ case CAR:
+ case '"':
+ case '\\': {
+ str_len += 2;
+ break;
+ }
+ default: {
+ if (ch > 0x7F && shift == 1) {
+ emsgf(_("E474: String \"%.*s\" contains byte that does not start "
+ "any UTF-8 character"),
+ utf_len - (i - shift), utf_buf + i - shift);
+ xfree(tofree);
+ return FAIL;
+ } else if ((SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END)
+ || (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END)) {
+ emsgf(_("E474: UTF-8 string contains code point which belongs "
+ "to a surrogate pair: %.*s"),
+ utf_len - (i - shift), utf_buf + i - shift);
+ xfree(tofree);
+ return FAIL;
+ } else if (ENCODE_RAW(p_enc_conv, ch)) {
+ str_len += shift;
+ } else {
+ str_len += ((sizeof("\\u1234") - 1)
+ * (size_t) (1 + (ch >= SURROGATE_FIRST_CHAR)));
+ }
+ break;
+ }
+ }
+ }
+ ga_append(gap, '"');
+ ga_grow(gap, (int) str_len);
+ for (size_t i = 0; i < utf_len;) {
+ const int ch = utf_ptr2char(utf_buf + i);
+ const size_t shift = (ch == 0? 1: utf_char2len(ch));
+ assert(shift > 0);
+ // Is false on invalid unicode, but this should already be handled.
+ assert(ch == 0 || shift == utf_ptr2len(utf_buf + i));
+ switch (ch) {
+ case BS:
+ case TAB:
+ case NL:
+ case FF:
+ case CAR:
+ case '"':
+ case '\\': {
+ ga_concat_len(gap, escapes[ch], 2);
+ break;
+ }
+ default: {
+ if (ENCODE_RAW(p_enc_conv, ch)) {
+ ga_concat_len(gap, utf_buf + i, shift);
+ } else if (ch < SURROGATE_FIRST_CHAR) {
+ ga_concat_len(gap, ((const char[]) {
+ '\\', 'u',
+ xdigits[(ch >> (4 * 3)) & 0xF],
+ xdigits[(ch >> (4 * 2)) & 0xF],
+ xdigits[(ch >> (4 * 1)) & 0xF],
+ xdigits[(ch >> (4 * 0)) & 0xF],
+ }), sizeof("\\u1234") - 1);
+ } else {
+ const int tmp = ch - SURROGATE_FIRST_CHAR;
+ const int hi = SURROGATE_HI_START + ((tmp >> 10) & ((1 << 10) - 1));
+ const int lo = SURROGATE_LO_END + ((tmp >> 0) & ((1 << 10) - 1));
+ ga_concat_len(gap, ((const char[]) {
+ '\\', 'u',
+ xdigits[(hi >> (4 * 3)) & 0xF],
+ xdigits[(hi >> (4 * 2)) & 0xF],
+ xdigits[(hi >> (4 * 1)) & 0xF],
+ xdigits[(hi >> (4 * 0)) & 0xF],
+ '\\', 'u',
+ xdigits[(lo >> (4 * 3)) & 0xF],
+ xdigits[(lo >> (4 * 2)) & 0xF],
+ xdigits[(lo >> (4 * 1)) & 0xF],
+ xdigits[(lo >> (4 * 0)) & 0xF],
+ }), (sizeof("\\u1234") - 1) * 2);
+ }
+ break;
+ }
+ }
+ i += shift;
+ }
+ ga_append(gap, '"');
+ xfree(tofree);
+ }
+ return OK;
+}
+
+#undef CONV_STRING
+#define CONV_STRING(buf, len) \
+ do { \
+ if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \
+ return FAIL; \
+ } \
+ } while (0)
+
+#undef CONV_EXT_STRING
+#define CONV_EXT_STRING(buf, len, type) \
+ do { \
+ xfree(buf); \
+ EMSG(_("E474: Unable to convert EXT string to JSON")); \
+ return FAIL; \
+ } while (0)
+
+#undef CONV_FUNC
+#define CONV_FUNC(fun) \
+ return conv_error(_("E474: Error while dumping %s, %s: " \
+ "attempt to dump function reference"), \
+ mpstack, objname)
+
+/// Check whether given key can be used in json_encode()
+///
+/// @param[in] tv Key to check.
+static inline bool check_json_key(const typval_T *const tv)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if (tv->v_type == VAR_STRING) {
+ return true;
+ }
+ if (tv->v_type != VAR_DICT) {
+ return false;
+ }
+ const dict_T *const spdict = tv->vval.v_dict;
+ if (spdict->dv_hashtab.ht_used != 2) {
+ return false;
+ }
+ const dictitem_T *type_di;
+ const dictitem_T *val_di;
+ if ((type_di = dict_find((dict_T *) spdict, (char_u *) "_TYPE", -1)) == NULL
+ || type_di->di_tv.v_type != VAR_LIST
+ || (type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPString]
+ && type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPBinary])
+ || (val_di = dict_find((dict_T *) spdict, (char_u *) "_VAL", -1)) == NULL
+ || val_di->di_tv.v_type != VAR_LIST) {
+ return false;
+ }
+ if (val_di->di_tv.vval.v_list == NULL) {
+ return true;
+ }
+ for (const listitem_T *li = val_di->di_tv.vval.v_list->lv_first;
+ li != NULL; li = li->li_next) {
+ if (li->li_tv.v_type != VAR_STRING) {
+ return false;
+ }
+ }
+ return true;
+}
+
+#undef CONV_SPECIAL_DICT_KEY_CHECK
+#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) \
+ do { \
+ if (!check_json_key(&kv_pair->lv_first->li_tv)) { \
+ EMSG(_("E474: Invalid key in special dictionary")); \
+ goto encode_vim_to_##name##_error_ret; \
+ } \
+ } while (0)
+
+DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap)
+
+#undef CONV_STRING
+#undef CONV_STR_STRING
+#undef CONV_EXT_STRING
+#undef CONV_NUMBER
+#undef CONV_FLOAT
+#undef CONV_FUNC
+#undef CONV_EMPTY_LIST
+#undef CONV_LIST_START
+#undef CONV_EMPTY_DICT
+#undef CONV_NIL
+#undef CONV_BOOL
+#undef CONV_UNSIGNED_NUMBER
+#undef CONV_DICT_START
+#undef CONV_DICT_END
+#undef CONV_DICT_AFTER_KEY
+#undef CONV_DICT_BETWEEN_ITEMS
+#undef CONV_SPECIAL_DICT_KEY_CHECK
+#undef CONV_LIST_END
+#undef CONV_LIST_BETWEEN_ITEMS
+#undef CONV_RECURSE
+#undef CONV_ALLOW_SPECIAL
+
+/// Return a string with the string representation of a variable.
+/// Puts quotes around strings, so that they can be parsed back by eval().
+///
+/// @param[in] tv typval_T to convert.
+/// @param[out] len Location where length of the result will be saved.
+///
+/// @return String representation of the variable or NULL.
+char *encode_tv2string(typval_T *tv, size_t *len)
+ FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
+{
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 80);
+ encode_vim_to_string(&ga, tv, "encode_tv2string() argument");
+ did_echo_string_emsg = false;
+ if (len != NULL) {
+ *len = (size_t) ga.ga_len;
+ }
+ ga_append(&ga, '\0');
+ return (char *) ga.ga_data;
+}
+
+/// Return a string with the string representation of a variable.
+/// Does not put quotes around strings, as ":echo" displays values.
+///
+/// @param[in] tv typval_T to convert.
+/// @param[out] len Location where length of the result will be saved.
+///
+/// @return String representation of the variable or NULL.
+char *encode_tv2echo(typval_T *tv, size_t *len)
+ FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
+{
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 80);
+ if (tv->v_type == VAR_STRING || tv->v_type == VAR_FUNC) {
+ if (tv->vval.v_string != NULL) {
+ ga_concat(&ga, tv->vval.v_string);
+ }
+ } else {
+ encode_vim_to_echo(&ga, tv, ":echo argument");
+ }
+ if (len != NULL) {
+ *len = (size_t) ga.ga_len;
+ }
+ ga_append(&ga, '\0');
+ return (char *) ga.ga_data;
+}
+
+/// Return a string with the string representation of a variable.
+/// Puts quotes around strings, so that they can be parsed back by eval().
+///
+/// @param[in] tv typval_T to convert.
+/// @param[out] len Location where length of the result will be saved.
+///
+/// @return String representation of the variable or NULL.
+char *encode_tv2json(typval_T *tv, size_t *len)
+ FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
+{
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 80);
+ encode_vim_to_json(&ga, tv, "encode_tv2json() argument");
+ did_echo_string_emsg = false;
+ if (len != NULL) {
+ *len = (size_t) ga.ga_len;
+ }
+ ga_append(&ga, '\0');
+ return (char *) ga.ga_data;
+}
+
+#define CONV_STRING(buf, len) \
+ do { \
+ if (buf == NULL) { \
+ msgpack_pack_bin(packer, 0); \
+ } else { \
+ const size_t len_ = (len); \
+ msgpack_pack_bin(packer, len_); \
+ msgpack_pack_bin_body(packer, buf, len_); \
+ } \
+ } while (0)
+
+#define CONV_STR_STRING(buf, len) \
+ do { \
+ if (buf == NULL) { \
+ msgpack_pack_str(packer, 0); \
+ } else { \
+ const size_t len_ = (len); \
+ msgpack_pack_str(packer, len_); \
+ msgpack_pack_str_body(packer, buf, len_); \
+ } \
+ } while (0)
+
+#define CONV_EXT_STRING(buf, len, type) \
+ do { \
+ if (buf == NULL) { \
+ msgpack_pack_ext(packer, 0, (int8_t) type); \
+ } else { \
+ const size_t len_ = (len); \
+ msgpack_pack_ext(packer, len_, (int8_t) type); \
+ msgpack_pack_ext_body(packer, buf, len_); \
+ } \
+ } while (0)
+
+#define CONV_NUMBER(num) \
+ msgpack_pack_int64(packer, (int64_t) (num))
+
+#define CONV_FLOAT(flt) \
+ msgpack_pack_double(packer, (double) (flt))
+
+#define CONV_FUNC(fun) \
+ return conv_error(_("E951: Error while dumping %s, %s: " \
+ "attempt to dump function reference"), \
+ mpstack, objname)
+
+#define CONV_EMPTY_LIST() \
+ msgpack_pack_array(packer, 0)
+
+#define CONV_LIST_START(lst) \
+ msgpack_pack_array(packer, (size_t) (lst)->lv_len)
+
+#define CONV_EMPTY_DICT() \
+ msgpack_pack_map(packer, 0)
+
+#define CONV_NIL() \
+ msgpack_pack_nil(packer)
+
+#define CONV_BOOL(num) \
+ do { \
+ if ((num)) { \
+ msgpack_pack_true(packer); \
+ } else { \
+ msgpack_pack_false(packer); \
+ } \
+ } while (0)
+
+#define CONV_UNSIGNED_NUMBER(num) \
+ msgpack_pack_uint64(packer, (num))
+
+#define CONV_DICT_START(len) \
+ msgpack_pack_map(packer, (size_t) (len))
+
+#define CONV_DICT_END()
+
+#define CONV_DICT_AFTER_KEY()
+
+#define CONV_DICT_BETWEEN_ITEMS()
+
+#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair)
+
+#define CONV_LIST_END(lst)
+
+#define CONV_LIST_BETWEEN_ITEMS()
+
+#define CONV_RECURSE(val, conv_type) \
+ return conv_error(_("E952: Unable to dump %s: " \
+ "container references itself in %s"), \
+ mpstack, objname)
+
+#define CONV_ALLOW_SPECIAL true
+
+DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
+
+#undef CONV_STRING
+#undef CONV_STR_STRING
+#undef CONV_EXT_STRING
+#undef CONV_NUMBER
+#undef CONV_FLOAT
+#undef CONV_FUNC
+#undef CONV_EMPTY_LIST
+#undef CONV_LIST_START
+#undef CONV_EMPTY_DICT
+#undef CONV_NIL
+#undef CONV_BOOL
+#undef CONV_UNSIGNED_NUMBER
+#undef CONV_DICT_START
+#undef CONV_DICT_END
+#undef CONV_DICT_AFTER_KEY
+#undef CONV_DICT_BETWEEN_ITEMS
+#undef CONV_SPECIAL_DICT_KEY_CHECK
+#undef CONV_LIST_END
+#undef CONV_LIST_BETWEEN_ITEMS
+#undef CONV_RECURSE
+#undef CONV_ALLOW_SPECIAL
diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h
new file mode 100644
index 0000000000..9bc665253b
--- /dev/null
+++ b/src/nvim/eval/encode.h
@@ -0,0 +1,75 @@
+#ifndef NVIM_EVAL_ENCODE_H
+#define NVIM_EVAL_ENCODE_H
+
+#include <stddef.h>
+
+#include <msgpack.h>
+
+#include "nvim/eval.h"
+#include "nvim/garray.h"
+#include "nvim/vim.h" // For STRLEN
+
+/// Convert VimL value to msgpack string
+///
+/// @param[out] packer Packer to save results in.
+/// @param[in] tv Dumped value.
+/// @param[in] objname Object name, used for error message.
+///
+/// @return OK in case of success, FAIL otherwise.
+int encode_vim_to_msgpack(msgpack_packer *const packer,
+ typval_T *const tv,
+ const char *const objname);
+
+/// Convert VimL value to :echo output
+///
+/// @param[out] packer Packer to save results in.
+/// @param[in] tv Dumped value.
+/// @param[in] objname Object name, used for error message.
+///
+/// @return OK in case of success, FAIL otherwise.
+int encode_vim_to_echo(garray_T *const packer,
+ typval_T *const tv,
+ const char *const objname);
+
+/// Structure defining state for read_from_list()
+typedef struct {
+ const listitem_T *li; ///< Item currently read.
+ size_t offset; ///< Byte offset inside the read item.
+ size_t li_length; ///< Length of the string inside the read item.
+} ListReaderState;
+
+/// Initialize ListReaderState structure
+static inline ListReaderState encode_init_lrstate(const list_T *const list)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return (ListReaderState) {
+ .li = list->lv_first,
+ .offset = 0,
+ .li_length = (list->lv_first->li_tv.vval.v_string == NULL
+ ? 0
+ : STRLEN(list->lv_first->li_tv.vval.v_string)),
+ };
+}
+
+/// Array mapping values from SpecialVarValue enum to names
+extern const char *const encode_special_var_names[];
+
+/// First codepoint in high surrogates block
+#define SURROGATE_HI_START 0xD800
+
+/// Last codepoint in high surrogates block
+#define SURROGATE_HI_END 0xDBFF
+
+/// First codepoint in low surrogates block
+#define SURROGATE_LO_START 0xDC00
+
+/// Last codepoint in low surrogates block
+#define SURROGATE_LO_END 0xDFFF
+
+/// First character that needs to be encoded as surrogate pair
+#define SURROGATE_FIRST_CHAR 0x10000
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/encode.h.generated.h"
+#endif
+#endif // NVIM_EVAL_ENCODE_H
diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h
index cdad1f3197..8ffc0c98ce 100644
--- a/src/nvim/eval_defs.h
+++ b/src/nvim/eval_defs.h
@@ -16,39 +16,52 @@ typedef double float_T;
typedef struct listvar_S list_T;
typedef struct dictvar_S dict_T;
-/*
- * Structure to hold an internal variable without a name.
- */
+/// Special variable values
+typedef enum {
+ kSpecialVarFalse, ///< v:false
+ kSpecialVarTrue, ///< v:true
+ kSpecialVarNull, ///< v:null
+} SpecialVarValue;
+
+/// Variable lock status for typval_T.v_lock
+typedef enum {
+ VAR_UNLOCKED = 0, ///< Not locked.
+ VAR_LOCKED, ///< User lock, can be unlocked.
+ VAR_FIXED, ///< Locked forever.
+} VarLockStatus;
+
+/// VimL variable types, for use in typval_T.v_type
+typedef enum {
+ VAR_UNKNOWN = 0, ///< Unknown (unspecified) value.
+ VAR_NUMBER, ///< Number, .v_number is used.
+ VAR_STRING, ///< String, .v_string is used.
+ VAR_FUNC, ///< Function referene, .v_string is used for function name.
+ VAR_LIST, ///< List, .v_list is used.
+ VAR_DICT, ///< Dictionary, .v_dict is used.
+ VAR_FLOAT, ///< Floating-point value, .v_float is used.
+ VAR_SPECIAL, ///< Special value (true, false, null), .v_special
+ ///< is used.
+} VarType;
+
+/// Structure that holds an internal variable value
typedef struct {
- char v_type; /* see below: VAR_NUMBER, VAR_STRING, etc. */
- char v_lock; /* see below: VAR_LOCKED, VAR_FIXED */
+ VarType v_type; ///< Variable type.
+ VarLockStatus v_lock; ///< Variable lock status.
union {
- varnumber_T v_number; /* number value */
- float_T v_float; /* floating number value */
- char_u *v_string; /* string value (can be NULL!) */
- list_T *v_list; /* list value (can be NULL!) */
- dict_T *v_dict; /* dict value (can be NULL!) */
- } vval;
+ varnumber_T v_number; ///< Number, for VAR_NUMBER.
+ SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL.
+ float_T v_float; ///< Floating-point number, for VAR_FLOAT.
+ char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL.
+ list_T *v_list; ///< List for VAR_LIST, can be NULL.
+ dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL.
+ } vval; ///< Actual value.
} typval_T;
-/* Values for "v_type". */
-#define VAR_UNKNOWN 0
-#define VAR_NUMBER 1 /* "v_number" is used */
-#define VAR_STRING 2 /* "v_string" is used */
-#define VAR_FUNC 3 /* "v_string" is function name */
-#define VAR_LIST 4 /* "v_list" is used */
-#define VAR_DICT 5 /* "v_dict" is used */
-#define VAR_FLOAT 6 /* "v_float" is used */
-
/* Values for "dv_scope". */
#define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */
#define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not
allowed to mask existing functions */
-/* Values for "v_lock". */
-#define VAR_LOCKED 1 /* locked with lock(), can use unlock() */
-#define VAR_FIXED 2 /* locked forever */
-
/*
* Structure to hold an item of a list: an internal variable without a name.
*/
@@ -107,19 +120,18 @@ typedef struct dictitem_S dictitem_T;
#define DI_FLAGS_LOCK 8 // "di_flags" value: locked variable
#define DI_FLAGS_ALLOC 16 // "di_flags" value: separately allocated
-/*
- * Structure to hold info about a Dictionary.
- */
+/// Structure representing a Dictionary
struct dictvar_S {
- char dv_lock; /* zero, VAR_LOCKED, VAR_FIXED */
- char dv_scope; /* zero, VAR_SCOPE, VAR_DEF_SCOPE */
- int dv_refcount; /* reference count */
- int dv_copyID; /* ID used by deepcopy() */
- hashtab_T dv_hashtab; /* hashtab that refers to the items */
- dict_T *dv_copydict; /* copied dict used by deepcopy() */
- dict_T *dv_used_next; /* next dict in used dicts list */
- dict_T *dv_used_prev; /* previous dict in used dicts list */
- QUEUE watchers; // dictionary key watchers set by user code
+ VarLockStatus dv_lock; ///< Whole dictionary lock status.
+ char dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if
+ ///< dictionary represents a scope (i.e. g:, l: …).
+ int dv_refcount; ///< Reference count.
+ int dv_copyID; ///< ID used when recursivery traversing a value.
+ hashtab_T dv_hashtab; ///< Hashtab containing all items.
+ dict_T *dv_copydict; ///< Copied dict used by deepcopy().
+ dict_T *dv_used_next; ///< Next dictionary in used dictionaries list.
+ dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list.
+ QUEUE watchers; ///< Dictionary key watchers set by user code.
};
// structure used for explicit stack while garbage collecting hash tables
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index e6012595fd..9bb62891c7 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -187,9 +187,9 @@ int process_wait(Process *proc, int ms, Queue *events) FUNC_ATTR_NONNULL_ARG(1)
// being freed) before we have a chance to get the status.
proc->refcount++;
LOOP_PROCESS_EVENTS_UNTIL(proc->loop, events, ms,
- // Until...
- got_int || // interrupted by the user
- proc->refcount == 1); // job exited
+ // Until...
+ got_int // interrupted by the user
+ || proc->refcount == 1); // job exited
// we'll assume that a user frantically hitting interrupt doesn't like
// the current job. Signal that it has to be killed.
diff --git a/src/nvim/event/time.c b/src/nvim/event/time.c
index 7bf333bcea..f68a66345f 100644
--- a/src/nvim/event/time.c
+++ b/src/nvim/event/time.c
@@ -17,6 +17,7 @@ void time_watcher_init(Loop *loop, TimeWatcher *watcher, void *data)
watcher->uv.data = watcher;
watcher->data = data;
watcher->events = loop->fast_events;
+ watcher->blockable = false;
}
void time_watcher_start(TimeWatcher *watcher, time_cb cb, uint64_t timeout,
@@ -50,6 +51,10 @@ static void time_watcher_cb(uv_timer_t *handle)
FUNC_ATTR_NONNULL_ALL
{
TimeWatcher *watcher = handle->data;
+ if (watcher->blockable && !queue_empty(watcher->events)) {
+ // the timer blocked and there already is an unprocessed event waiting
+ return;
+ }
CREATE_EVENT(watcher->events, time_event, 1, watcher);
}
diff --git a/src/nvim/event/time.h b/src/nvim/event/time.h
index 7882b2b627..14df176ea3 100644
--- a/src/nvim/event/time.h
+++ b/src/nvim/event/time.h
@@ -13,6 +13,7 @@ struct time_watcher {
void *data;
time_cb cb, close_cb;
Queue *events;
+ bool blockable;
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 4d62dd0ff9..86f1a16216 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3,6 +3,7 @@
*/
#include <assert.h>
+#include <float.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
@@ -50,7 +51,6 @@
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
-#include "nvim/tempfile.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/window.h"
@@ -274,17 +274,26 @@ static int linelen(int *has_tab)
static char_u *sortbuf1;
static char_u *sortbuf2;
-static int sort_ic; /* ignore case */
-static int sort_nr; /* sort on number */
-static int sort_rx; /* sort on regex instead of skipping it */
+static int sort_ic; ///< ignore case
+static int sort_nr; ///< sort on number
+static int sort_rx; ///< sort on regex instead of skipping it
+static int sort_flt; ///< sort on floating number
-static int sort_abort; /* flag to indicate if sorting has been interrupted */
+static int sort_abort; ///< flag to indicate if sorting has been interrupted
-/* Struct to store info to be sorted. */
+/// Struct to store info to be sorted.
typedef struct {
- linenr_T lnum; /* line number */
- long start_col_nr; /* starting column number or number */
- long end_col_nr; /* ending column number */
+ linenr_T lnum; ///< line number
+ long start_col_nr; ///< starting column number or number
+ long end_col_nr; ///< ending column number
+ union {
+ struct {
+ long start_col_nr; ///< starting column number
+ long end_col_nr; ///< ending column number
+ } line;
+ long value; ///< value if sorting by integer
+ float_T value_flt; ///< value if sorting by float
+ } st_u;
} sorti_T;
@@ -303,21 +312,26 @@ static int sort_compare(const void *s1, const void *s2)
if (got_int)
sort_abort = TRUE;
- /* When sorting numbers "start_col_nr" is the number, not the column
- * number. */
- if (sort_nr)
- result = l1.start_col_nr == l2.start_col_nr ? 0
- : l1.start_col_nr > l2.start_col_nr ? 1 : -1;
- else {
- /* We need to copy one line into "sortbuf1", because there is no
- * guarantee that the first pointer becomes invalid when obtaining the
- * second one. */
- STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.start_col_nr,
- l1.end_col_nr - l1.start_col_nr + 1);
- sortbuf1[l1.end_col_nr - l1.start_col_nr] = 0;
- STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.start_col_nr,
- l2.end_col_nr - l2.start_col_nr + 1);
- sortbuf2[l2.end_col_nr - l2.start_col_nr] = 0;
+ // When sorting numbers "start_col_nr" is the number, not the column
+ // number.
+ if (sort_nr) {
+ result = l1.st_u.value == l2.st_u.value
+ ? 0 : l1.st_u.value > l2.st_u.value
+ ? 1 : -1;
+ } else if (sort_flt) {
+ result = l1.st_u.value_flt == l2.st_u.value_flt
+ ? 0 : l1.st_u.value_flt > l2.st_u.value_flt
+ ? 1 : -1;
+ } else {
+ // We need to copy one line into "sortbuf1", because there is no
+ // guarantee that the first pointer becomes invalid when obtaining the
+ // second one.
+ STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.st_u.line.start_col_nr,
+ l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1);
+ sortbuf1[l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr] = 0;
+ STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.st_u.line.start_col_nr,
+ l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1);
+ sortbuf2[l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr] = 0;
result = sort_ic ? STRICMP(sortbuf1, sortbuf2)
: STRCMP(sortbuf1, sortbuf2);
@@ -361,7 +375,7 @@ void ex_sort(exarg_T *eap)
regmatch.regprog = NULL;
sorti_T *nrs = xmalloc(count * sizeof(sorti_T));
- sort_abort = sort_ic = sort_rx = sort_nr = 0;
+ sort_abort = sort_ic = sort_rx = sort_nr = sort_flt = 0;
size_t format_found = 0;
for (p = eap->arg; *p != NUL; ++p) {
@@ -371,7 +385,10 @@ void ex_sort(exarg_T *eap)
} else if (*p == 'r') {
sort_rx = true;
} else if (*p == 'n') {
- sort_nr = 2;
+ sort_nr = 1;
+ format_found++;
+ } else if (*p == 'f') {
+ sort_flt = 1;
format_found++;
} else if (*p == 'b') {
sort_what = STR2NR_BIN + STR2NR_FORCE;
@@ -424,7 +441,8 @@ void ex_sort(exarg_T *eap)
goto sortend;
}
- // From here on "sort_nr" is used as a flag for any number sorting.
+ // From here on "sort_nr" is used as a flag for any integer number
+ // sorting.
sort_nr += sort_what;
// Make an array with all line numbers. This avoids having to copy all
@@ -453,7 +471,7 @@ void ex_sort(exarg_T *eap)
end_col = 0;
}
- if (sort_nr) {
+ if (sort_nr || sort_flt) {
// Make sure vim_str2nr doesn't read any digits past the end
// of the match, by temporarily terminating the string there
s2 = s + end_col;
@@ -461,29 +479,42 @@ void ex_sort(exarg_T *eap)
*s2 = NUL;
// Sorting on number: Store the number itself.
p = s + start_col;
- if (sort_what & STR2NR_HEX) {
- s = skiptohex(p);
- } else if (sort_what & STR2NR_BIN) {
- s = (char_u*) skiptobin((char*) p);
- } else {
- s = skiptodigit(p);
- }
- if (s > p && s[-1] == '-') {
- // include preceding negative sign
- s--;
- }
- if (*s == NUL) {
- // empty line should sort before any number
- nrs[lnum - eap->line1].start_col_nr = -MAXLNUM;
+ if (sort_nr) {
+ if (sort_what & STR2NR_HEX) {
+ s = skiptohex(p);
+ } else if (sort_what & STR2NR_BIN) {
+ s = (char_u *)skiptobin((char *)p);
+ } else {
+ s = skiptodigit(p);
+ }
+ if (s > p && s[-1] == '-') {
+ s--; // include preceding negative sign
+ }
+ if (*s == NUL) {
+ // empty line should sort before any number
+ nrs[lnum - eap->line1].st_u.value = -MAXLNUM;
+ } else {
+ vim_str2nr(s, NULL, NULL, sort_what,
+ &nrs[lnum - eap->line1].st_u.value, NULL, 0);
+ }
} else {
- vim_str2nr(s, NULL, NULL, sort_what,
- &nrs[lnum - eap->line1].start_col_nr, NULL, 0);
+ s = skipwhite(p);
+ if (*s == '+') {
+ s = skipwhite(s + 1);
+ }
+
+ if (*s == NUL) {
+ // empty line should sort before any number
+ nrs[lnum - eap->line1].st_u.value_flt = -DBL_MAX;
+ } else {
+ nrs[lnum - eap->line1].st_u.value_flt = strtod((char *)s, NULL);
+ }
}
*s2 = c;
} else {
// Store the column to sort at.
- nrs[lnum - eap->line1].start_col_nr = start_col;
- nrs[lnum - eap->line1].end_col_nr = end_col;
+ nrs[lnum - eap->line1].st_u.line.start_col_nr = start_col;
+ nrs[lnum - eap->line1].st_u.line.end_col_nr = end_col;
}
nrs[lnum - eap->line1].lnum = lnum;
@@ -619,13 +650,13 @@ void ex_retab(exarg_T *eap)
num_tabs += num_spaces / new_ts;
num_spaces -= (num_spaces / new_ts) * new_ts;
}
- if (curbuf->b_p_et || got_tab ||
- (num_spaces + num_tabs < len)) {
- if (did_undo == FALSE) {
- did_undo = TRUE;
+ if (curbuf->b_p_et || got_tab
+ || (num_spaces + num_tabs < len)) {
+ if (did_undo == false) {
+ did_undo = true;
if (u_save((linenr_T)(lnum - 1),
- (linenr_T)(lnum + 1)) == FAIL) {
- new_line = NULL; /* flag out-of-memory */
+ (linenr_T)(lnum + 1)) == FAIL) {
+ new_line = NULL; // flag out-of-memory
break;
}
}
@@ -1326,15 +1357,17 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
#endif
size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL.
-
+
len += is_fish_shell ? sizeof("begin; ""; end") - 1
: sizeof("("")") - 1;
- if (itmp != NULL)
+ if (itmp != NULL) {
len += STRLEN(itmp) + sizeof(" { "" < "" } ") - 1;
- if (otmp != NULL)
+ }
+ if (otmp != NULL) {
len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "),
- char_u *buf = xmalloc(len);
+ }
+ char *const buf = xmalloc(len);
#if defined(UNIX)
// Put delimiters around the command (for concatenated commands) when
@@ -1342,19 +1375,19 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
if (itmp != NULL || otmp != NULL) {
char *fmt = is_fish_shell ? "begin; %s; end"
: "(%s)";
- vim_snprintf((char *)buf, len, fmt, (char *)cmd);
+ vim_snprintf(buf, len, fmt, (char *)cmd);
} else {
- STRCPY(buf, cmd);
+ strncpy(buf, (char *) cmd, len);
}
if (itmp != NULL) {
- STRCAT(buf, " < ");
- STRCAT(buf, itmp);
+ strncat(buf, " < ", len);
+ strncat(buf, (char *) itmp, len);
}
#else
// For shells that don't understand braces around commands, at least allow
// the use of commands in a pipe.
- STRCPY(buf, cmd);
+ strncpy(buf, cmd, len);
if (itmp != NULL) {
char_u *p;
@@ -1362,55 +1395,56 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
// Don't do this when 'shellquote' is not empty, otherwise the
// redirection would be inside the quotes.
if (*p_shq == NUL) {
- p = vim_strchr(buf, '|');
- if (p != NULL)
+ p = strchr(buf, '|');
+ if (p != NULL) {
*p = NUL;
+ }
}
- STRCAT(buf, " < ");
- STRCAT(buf, itmp);
+ strncat(buf, " < ", len);
+ strncat(buf, (char *) itmp, len);
if (*p_shq == NUL) {
- p = vim_strchr(cmd, '|');
+ p = strchr(cmd, '|');
if (p != NULL) {
- STRCAT(buf, " "); // Insert a space before the '|' for DOS
- STRCAT(buf, p);
+ strncat(buf, " ", len); // Insert a space before the '|' for DOS
+ strncat(buf, p, len);
}
}
}
#endif
- if (otmp != NULL)
- append_redir(buf, (int)len, p_srr, otmp);
-
- return buf;
+ if (otmp != NULL) {
+ append_redir(buf, len, (char *) p_srr, (char *) otmp);
+ }
+ return (char_u *) buf;
}
-/*
- * Append output redirection for file "fname" to the end of string buffer
- * "buf[buflen]"
- * Works with the 'shellredir' and 'shellpipe' options.
- * The caller should make sure that there is enough room:
- * STRLEN(opt) + STRLEN(fname) + 3
- */
-void append_redir(char_u *buf, int buflen, char_u *opt, char_u *fname)
+/// Append output redirection for the given file to the end of the buffer
+///
+/// @param[out] buf Buffer to append to.
+/// @param[in] buflen Buffer length.
+/// @param[in] opt Separator or format string to append: will append
+/// `printf(' ' . opt, fname)` if `%s` is found in `opt` or
+/// a space, opt, a space and then fname if `%s` is not found
+/// there.
+/// @param[in] fname File name to append.
+void append_redir(char *const buf, const size_t buflen,
+ const char *const opt, const char *const fname)
{
- char_u *p;
- char_u *end;
-
- end = buf + STRLEN(buf);
- /* find "%s" */
- for (p = opt; (p = vim_strchr(p, '%')) != NULL; ++p) {
- if (p[1] == 's') /* found %s */
+ char *const end = buf + strlen(buf);
+ // find "%s"
+ const char *p = opt;
+ for (; (p = strchr(p, '%')) != NULL; p++) {
+ if (p[1] == 's') { // found %s
break;
- if (p[1] == '%') /* skip %% */
- ++p;
+ } else if (p[1] == '%') { // skip %%
+ p++;
+ }
}
if (p != NULL) {
- *end = ' '; /* not really needed? Not with sh, ksh or bash */
- vim_snprintf((char *)end + 1, (size_t)(buflen - (end + 1 - buf)),
- (char *)opt, (char *)fname);
- } else
- vim_snprintf((char *)end, (size_t)(buflen - (end - buf)),
- " %s %s",
- (char *)opt, (char *)fname);
+ *end = ' '; // not really needed? Not with sh, ksh or bash
+ vim_snprintf(end + 1, (size_t) (buflen - (end + 1 - buf)), opt, fname);
+ } else {
+ vim_snprintf(end, (size_t) (buflen - (end - buf)), " %s %s", opt, fname);
+ }
}
void print_line_no_prefix(linenr_T lnum, int use_number, int list)
@@ -1506,8 +1540,11 @@ void ex_file(exarg_T *eap)
if (rename_buffer(eap->arg) == FAIL)
return;
}
- /* print full file name if :cd used */
- fileinfo(FALSE, FALSE, eap->forceit);
+
+ if (!shortmess(SHM_FILEINFO)) {
+ // print full file name if :cd used
+ fileinfo(false, false, eap->forceit);
+ }
}
/*
@@ -1586,15 +1623,14 @@ int do_write(exarg_T *eap)
}
}
- /*
- * Writing to the current file is not allowed in readonly mode
- * and a file name is required.
- * "nofile" and "nowrite" buffers cannot be written implicitly either.
- */
- if (!other && (
- bt_dontwrite_msg(curbuf) ||
- check_fname() == FAIL || check_readonly(&eap->forceit, curbuf)))
+ // Writing to the current file is not allowed in readonly mode
+ // and a file name is required.
+ // "nofile" and "nowrite" buffers cannot be written implicitly either.
+ if (!other && (bt_dontwrite_msg(curbuf)
+ || check_fname() == FAIL
+ || check_readonly(&eap->forceit, curbuf))) {
goto theend;
+ }
if (!other) {
ffname = curbuf->b_ffname;
@@ -2090,15 +2126,13 @@ do_ecmd (
if ((command != NULL || newlnum > (linenr_T)0)
&& *get_vim_var_str(VV_SWAPCOMMAND) == NUL) {
- char_u *p;
-
- /* Set v:swapcommand for the SwapExists autocommands. */
- size_t len = (command != NULL) ? STRLEN(command) + 3 : 30;
- p = xmalloc(len);
+ // Set v:swapcommand for the SwapExists autocommands.
+ const size_t len = (command != NULL) ? STRLEN(command) + 3 : 30;
+ char *const p = xmalloc(len);
if (command != NULL) {
- vim_snprintf((char *)p, len, ":%s\r", command);
+ vim_snprintf(p, len, ":%s\r", command);
} else {
- vim_snprintf((char *)p, len, "%" PRId64 "G", (int64_t)newlnum);
+ vim_snprintf(p, len, "%" PRId64 "G", (int64_t)newlnum);
}
set_vim_var_string(VV_SWAPCOMMAND, p, -1);
did_set_swapcommand = TRUE;
@@ -2223,16 +2257,15 @@ do_ecmd (
delbuf_msg(new_name); /* frees new_name */
goto theend;
}
- if (buf == curbuf) /* already in new buffer */
- auto_buf = TRUE;
- else {
- /*
- * <VN> We could instead free the synblock
- * and re-attach to buffer, perhaps.
- */
- if (curwin->w_buffer != NULL &&
- curwin->w_s == &(curwin->w_buffer->b_s))
+ if (buf == curbuf) { // already in new buffer
+ auto_buf = true;
+ } else {
+ // <VN> We could instead free the synblock
+ // and re-attach to buffer, perhaps.
+ if (curwin->w_buffer != NULL
+ && curwin->w_s == &(curwin->w_buffer->b_s)) {
curwin->w_s = &(buf->b_s);
+ }
curwin->w_buffer = buf;
curbuf = buf;
@@ -2259,11 +2292,11 @@ do_ecmd (
curwin->w_pcmark.lnum = 1;
curwin->w_pcmark.col = 0;
- } else { /* !other_file */
- if (
- (flags & ECMD_ADDBUF) ||
- check_fname() == FAIL)
+ } else { // !other_file
+ if ((flags & ECMD_ADDBUF)
+ || check_fname() == FAIL) {
goto theend;
+ }
oldbuf = (flags & ECMD_OLDBUF);
}
@@ -2483,7 +2516,9 @@ do_ecmd (
msg_scroll = msg_scroll_save;
msg_scrolled_ign = TRUE;
- fileinfo(FALSE, TRUE, FALSE);
+ if (!shortmess(SHM_FILEINFO)) {
+ fileinfo(false, true, false);
+ }
msg_scrolled_ign = FALSE;
}
@@ -3017,7 +3052,7 @@ void do_sub(exarg_T *eap)
// The number of lines joined is the number of lines in the range
linenr_T joined_lines_count = eap->line2 - eap->line1 + 1
// plus one extra line if not at the end of file.
- + eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0;
+ + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0);
if (joined_lines_count > 1) {
do_join(joined_lines_count, FALSE, TRUE, FALSE, true);
sub_nsubs = joined_lines_count - 1;
@@ -3781,16 +3816,17 @@ skip:
EMSG2(_(e_patnotf2), get_search_pat());
}
- if (do_ask && hasAnyFolding(curwin))
- /* Cursor position may require updating */
+ if (do_ask && hasAnyFolding(curwin)) {
+ // Cursor position may require updating
changed_window_setting();
+ }
- vim_regfree(regmatch.regprog);
+ vim_regfree(regmatch.regprog);
- // Restore the flag values, they can be used for ":&&".
- do_all = save_do_all;
- do_ask = save_do_ask;
- }
+ // Restore the flag values, they can be used for ":&&".
+ do_all = save_do_all;
+ do_ask = save_do_ask;
+}
/*
* Give message for number of substitutions.
@@ -4378,17 +4414,20 @@ int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_la
|| (arg[0] == '\\' && arg[1] == '{'))
*d++ = '\\';
- for (s = arg; *s; ++s) {
- /*
- * Replace "|" with "bar" and '"' with "quote" to match the name of
- * the tags for these commands.
- * Replace "*" with ".*" and "?" with "." to match command line
- * completion.
- * Insert a backslash before '~', '$' and '.' to avoid their
- * special meaning.
- */
- if (d - IObuff > IOSIZE - 10) /* getting too long!? */
+ // If tag starts with "('", skip the "(". Fixes CTRL-] on ('option'.
+ if (*arg == '(' && arg[1] == '\'') {
+ arg++;
+ }
+ for (s = arg; *s; s++) {
+ // Replace "|" with "bar" and '"' with "quote" to match the name of
+ // the tags for these commands.
+ // Replace "*" with ".*" and "?" with "." to match command line
+ // completion.
+ // Insert a backslash before '~', '$' and '.' to avoid their
+ // special meaning.
+ if (d - IObuff > IOSIZE - 10) { // getting too long!?
break;
+ }
switch (*s) {
case '|': STRCPY(d, "bar");
d += 3;
@@ -4449,6 +4488,12 @@ int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_la
*d++ = *s;
+ // If tag contains "({" or "([", tag terminates at the "(".
+ // This is for help on functions, e.g.: abs({expr}).
+ if (*s == '(' && (s[1] == '{' || s[1] =='[')) {
+ break;
+ }
+
/*
* If tag starts with ', toss everything after a second '. Fixes
* CTRL-] on 'option'. (would include the trailing '.').
@@ -4779,6 +4824,7 @@ void ex_helptags(exarg_T *eap)
WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
if (dirname == NULL || !os_isdir(dirname)) {
EMSG2(_("E150: Not a directory: %s"), eap->arg);
+ xfree(dirname);
return;
}
@@ -4876,16 +4922,13 @@ helptags_one (
int fi;
char_u *s;
char_u *fname;
- int dirlen;
int utf8 = MAYBE;
int this_utf8;
int firstline;
int mix = FALSE; /* detected mixed encodings */
- /*
- * Find all *.txt files.
- */
- dirlen = (int)STRLEN(dir);
+ // Find all *.txt files.
+ size_t dirlen = STRLEN(dir);
STRCPY(NameBuff, dir);
STRCAT(NameBuff, "/**/*");
STRCAT(NameBuff, ext);
@@ -4907,7 +4950,7 @@ helptags_one (
*/
STRCPY(NameBuff, dir);
add_pathsep((char *)NameBuff);
- STRCAT(NameBuff, tagfname);
+ STRNCAT(NameBuff, tagfname, sizeof(NameBuff) - dirlen - 2);
fd_tags = mch_fopen((char *)NameBuff, "w");
if (fd_tags == NULL) {
EMSG2(_("E152: Cannot open %s for writing"), NameBuff);
@@ -5013,7 +5056,9 @@ helptags_one (
/*
* Sort the tags.
*/
- sort_strings((char_u **)ga.ga_data, ga.ga_len);
+ if (ga.ga_data != NULL) {
+ sort_strings((char_u **)ga.ga_data, ga.ga_len);
+ }
/*
* Check for duplicates.
@@ -5781,13 +5826,14 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
switch (cmd_idx)
{
case SIGNCMD_DEFINE:
- if (STRNCMP(last, "texthl", p - last) == 0 ||
- STRNCMP(last, "linehl", p - last) == 0)
+ if (STRNCMP(last, "texthl", p - last) == 0
+ || STRNCMP(last, "linehl", p - last) == 0) {
xp->xp_context = EXPAND_HIGHLIGHT;
- else if (STRNCMP(last, "icon", p - last) == 0)
+ } else if (STRNCMP(last, "icon", p - last) == 0) {
xp->xp_context = EXPAND_FILES;
- else
+ } else {
xp->xp_context = EXPAND_NOTHING;
+ }
break;
case SIGNCMD_PLACE:
if (STRNCMP(last, "name", p - last) == 0)
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 6c8835b5c5..6c58879d58 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -88,7 +88,7 @@ return {
},
{
command='argadd',
- flags=bit.bor(BANG, NEEDARG, RANGE, NOTADR, ZEROR, FILES, TRLBAR),
+ flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, FILES, TRLBAR),
addr_type=ADDR_ARGUMENTS,
func='ex_argadd',
},
@@ -2575,6 +2575,18 @@ return {
func='ex_copymove',
},
{
+ command='tcd',
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ addr_type=ADDR_LINES,
+ func='ex_cd',
+ },
+ {
+ command='tchdir',
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ addr_type=ADDR_LINES,
+ func='ex_cd',
+ },
+ {
command='tNext',
flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR),
addr_type=ADDR_LINES,
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 47774f5a99..df4a6d52c4 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -57,23 +57,23 @@ typedef struct scriptitem_S {
char_u *sn_name;
bool file_id_valid;
FileID file_id;
- int sn_prof_on; /* TRUE when script is/was profiled */
- int sn_pr_force; /* forceit: profile functions in this script */
- proftime_T sn_pr_child; /* time set when going into first child */
- int sn_pr_nest; /* nesting for sn_pr_child */
- /* profiling the script as a whole */
- int sn_pr_count; /* nr of times sourced */
- proftime_T sn_pr_total; /* time spent in script + children */
- proftime_T sn_pr_self; /* time spent in script itself */
- proftime_T sn_pr_start; /* time at script start */
- proftime_T sn_pr_children; /* time in children after script start */
- /* profiling the script per line */
- garray_T sn_prl_ga; /* things stored for every line */
- proftime_T sn_prl_start; /* start time for current line */
- proftime_T sn_prl_children; /* time spent in children for this line */
- proftime_T sn_prl_wait; /* wait start time for current line */
- int sn_prl_idx; /* index of line being timed; -1 if none */
- int sn_prl_execed; /* line being timed was executed */
+ bool sn_prof_on; ///< true when script is/was profiled
+ int sn_pr_force; ///< forceit: profile functions in this script
+ proftime_T sn_pr_child; ///< time set when going into first child
+ int sn_pr_nest; ///< nesting for sn_pr_child
+ // profiling the script as a whole
+ int sn_pr_count; ///< nr of times sourced
+ proftime_T sn_pr_total; ///< time spent in script + children
+ proftime_T sn_pr_self; ///< time spent in script itself
+ proftime_T sn_pr_start; ///< time at script start
+ proftime_T sn_pr_children; ///< time in children after script start
+ // profiling the script per line
+ garray_T sn_prl_ga; ///< things stored for every line
+ proftime_T sn_prl_start; ///< start time for current line
+ proftime_T sn_prl_children; ///< time spent in children for this line
+ proftime_T sn_prl_wait; ///< wait start time for current line
+ linenr_T sn_prl_idx; ///< index of line being timed; -1 if none
+ int sn_prl_execed; ///< line being timed was executed
} scriptitem_T;
static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL};
@@ -81,9 +81,9 @@ static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL};
/* Struct used in sn_prl_ga for every line of a script. */
typedef struct sn_prl_S {
- int snp_count; /* nr of times line was executed */
- proftime_T sn_prl_total; /* time spent in a line + children */
- proftime_T sn_prl_self; /* time spent in a line itself */
+ int snp_count; ///< nr of times line was executed
+ proftime_T sn_prl_total; ///< time spent in a line + children
+ proftime_T sn_prl_self; ///< time spent in a line itself
} sn_prl_T;
/*
@@ -93,18 +93,18 @@ typedef struct sn_prl_S {
* sourcing can be done recursively.
*/
struct source_cookie {
- FILE *fp; /* opened file for sourcing */
- char_u *nextline; /* if not NULL: line that was read ahead */
- int finished; /* ":finish" used */
+ FILE *fp; ///< opened file for sourcing
+ char_u *nextline; ///< if not NULL: line that was read ahead
+ int finished; ///< ":finish" used
#if defined(USE_CRNL)
- int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
- int error; /* TRUE if LF found after CR-LF */
+ int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS
+ int error; ///< TRUE if LF found after CR-LF
#endif
- linenr_T breakpoint; /* next line with breakpoint or zero */
- char_u *fname; /* name of sourced file */
- int dbg_tick; /* debug_tick when breakpoint was set */
- int level; /* top nesting level of sourced file */
- vimconv_T conv; /* type of conversion */
+ linenr_T breakpoint; ///< next line with breakpoint or zero
+ char_u *fname; ///< name of sourced file
+ int dbg_tick; ///< debug_tick when breakpoint was set
+ int level; ///< top nesting level of sourced file
+ vimconv_T conv; ///< type of conversion
};
# define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)])
@@ -144,6 +144,10 @@ void do_debug(char_u *cmd)
#define CMD_FINISH 4
#define CMD_QUIT 5
#define CMD_INTERRUPT 6
+#define CMD_BACKTRACE 7
+#define CMD_FRAME 8
+#define CMD_UP 9
+#define CMD_DOWN 10
++RedrawingDisabled; /* don't redisplay the window */
@@ -185,6 +189,7 @@ void do_debug(char_u *cmd)
ignore_script = TRUE;
}
+ xfree(cmdline);
cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL);
if (typeahead_saved) {
@@ -194,6 +199,7 @@ void do_debug(char_u *cmd)
ex_normal_busy = save_ex_normal_busy;
cmdline_row = msg_row;
+ msg_starthere();
if (cmdline != NULL) {
/* If this is a debug command, set "last_cmd".
* If not, reset "last_cmd".
@@ -210,8 +216,15 @@ void do_debug(char_u *cmd)
case 's': last_cmd = CMD_STEP;
tail = "tep";
break;
- case 'f': last_cmd = CMD_FINISH;
- tail = "inish";
+ case 'f':
+ last_cmd = 0;
+ if (p[1] == 'r') {
+ last_cmd = CMD_FRAME;
+ tail = "rame";
+ } else {
+ last_cmd = CMD_FINISH;
+ tail = "inish";
+ }
break;
case 'q': last_cmd = CMD_QUIT;
tail = "uit";
@@ -219,6 +232,26 @@ void do_debug(char_u *cmd)
case 'i': last_cmd = CMD_INTERRUPT;
tail = "nterrupt";
break;
+ case 'b':
+ last_cmd = CMD_BACKTRACE;
+ if (p[1] == 't') {
+ tail = "t";
+ } else {
+ tail = "acktrace";
+ }
+ break;
+ case 'w':
+ last_cmd = CMD_BACKTRACE;
+ tail = "here";
+ break;
+ case 'u':
+ last_cmd = CMD_UP;
+ tail = "p";
+ break;
+ case 'd':
+ last_cmd = CMD_DOWN;
+ tail = "own";
+ break;
default: last_cmd = 0;
}
if (last_cmd != 0) {
@@ -228,8 +261,9 @@ void do_debug(char_u *cmd)
++p;
++tail;
}
- if (ASCII_ISALPHA(*p))
+ if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME) {
last_cmd = 0;
+ }
}
}
@@ -259,7 +293,28 @@ void do_debug(char_u *cmd)
/* Do not repeat ">interrupt" cmd, continue stepping. */
last_cmd = CMD_STEP;
break;
+ case CMD_BACKTRACE:
+ do_showbacktrace(cmd);
+ continue;
+ case CMD_FRAME:
+ if (*p == NUL) {
+ do_showbacktrace(cmd);
+ } else {
+ p = skipwhite(p);
+ do_setdebugtracelevel(p);
+ }
+ continue;
+ case CMD_UP:
+ debug_backtrace_level++;
+ do_checkbacktracelevel();
+ continue;
+ case CMD_DOWN:
+ debug_backtrace_level--;
+ do_checkbacktracelevel();
+ continue;
}
+ // Going out reset backtrace_level
+ debug_backtrace_level = 0;
break;
}
@@ -269,10 +324,8 @@ void do_debug(char_u *cmd)
(void)do_cmdline(cmdline, getexline, NULL,
DOCMD_VERBOSE|DOCMD_EXCRESET);
debug_break_level = n;
-
- xfree(cmdline);
}
- lines_left = Rows - 1;
+ lines_left = (int)(Rows - 1);
}
xfree(cmdline);
@@ -281,7 +334,7 @@ void do_debug(char_u *cmd)
redraw_all_later(NOT_VALID);
need_wait_return = FALSE;
msg_scroll = save_msg_scroll;
- lines_left = Rows - 1;
+ lines_left = (int)(Rows - 1);
State = save_State;
did_emsg = save_did_emsg;
cmd_silent = save_cmd_silent;
@@ -294,6 +347,78 @@ void do_debug(char_u *cmd)
debug_did_msg = TRUE;
}
+static int get_maxbacktrace_level(void)
+{
+ int maxbacktrace = 0;
+
+ if (sourcing_name != NULL) {
+ char *p = (char *)sourcing_name;
+ char *q;
+ while ((q = strstr(p, "..")) != NULL) {
+ p = q + 2;
+ maxbacktrace++;
+ }
+ }
+ return maxbacktrace;
+}
+
+static void do_setdebugtracelevel(char_u *arg)
+{
+ int level = atoi((char *)arg);
+ if (*arg == '+' || level < 0) {
+ debug_backtrace_level += level;
+ } else {
+ debug_backtrace_level = level;
+ }
+
+ do_checkbacktracelevel();
+}
+
+static void do_checkbacktracelevel(void)
+{
+ if (debug_backtrace_level < 0) {
+ debug_backtrace_level = 0;
+ MSG(_("frame is zero"));
+ } else {
+ int max = get_maxbacktrace_level();
+ if (debug_backtrace_level > max) {
+ debug_backtrace_level = max;
+ smsg(_("frame at highest level: %d"), max);
+ }
+ }
+}
+
+static void do_showbacktrace(char_u *cmd)
+{
+ if (sourcing_name != NULL) {
+ int i = 0;
+ int max = get_maxbacktrace_level();
+ char *cur = (char *)sourcing_name;
+ while (!got_int) {
+ char *next = strstr(cur, "..");
+ if (next != NULL) {
+ *next = NUL;
+ }
+ if (i == max - debug_backtrace_level) {
+ smsg("->%d %s", max - i, cur);
+ } else {
+ smsg(" %d %s", max - i, cur);
+ }
+ i++;
+ if (next == NULL) {
+ break;
+ }
+ *next = '.';
+ cur = next + 2;
+ }
+ }
+ if (sourcing_lnum != 0) {
+ smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd);
+ } else {
+ smsg(_("cmd: %s"), cmd);
+ }
+}
+
/*
* ":debug".
*/
@@ -392,12 +517,12 @@ int dbg_check_skipped(exarg_T *eap)
* This is a grow-array of structs.
*/
struct debuggy {
- int dbg_nr; /* breakpoint number */
- int dbg_type; /* DBG_FUNC or DBG_FILE */
- char_u *dbg_name; /* function or file name */
- regprog_T *dbg_prog; /* regexp program */
- linenr_T dbg_lnum; /* line number in function or file */
- int dbg_forceit; /* ! used */
+ int dbg_nr; ///< breakpoint number
+ int dbg_type; ///< DBG_FUNC or DBG_FILE
+ char_u *dbg_name; ///< function or file name
+ regprog_T *dbg_prog; ///< regexp program
+ linenr_T dbg_lnum; ///< line number in function or file
+ int dbg_forceit; ///< ! used
};
static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL};
@@ -432,14 +557,12 @@ dbg_parsearg (
bp = &DEBUGGY(gap, gap->ga_len);
- /* Find "func" or "file". */
- if (STRNCMP(p, "func", 4) == 0)
+ // Find "func" or "file".
+ if (STRNCMP(p, "func", 4) == 0) {
bp->dbg_type = DBG_FUNC;
- else if (STRNCMP(p, "file", 4) == 0)
+ } else if (STRNCMP(p, "file", 4) == 0) {
bp->dbg_type = DBG_FILE;
- else if (
- gap != &prof_ga &&
- STRNCMP(p, "here", 4) == 0) {
+ } else if (gap != &prof_ga && STRNCMP(p, "here", 4) == 0) {
if (curbuf->b_ffname == NULL) {
EMSG(_(e_noname));
return FAIL;
@@ -452,16 +575,15 @@ dbg_parsearg (
}
p = skipwhite(p + 4);
- /* Find optional line number. */
- if (here)
+ // Find optional line number.
+ if (here) {
bp->dbg_lnum = curwin->w_cursor.lnum;
- else if (
- gap != &prof_ga &&
- ascii_isdigit(*p)) {
+ } else if (gap != &prof_ga && ascii_isdigit(*p)) {
bp->dbg_lnum = getdigits_long(&p);
p = skipwhite(p);
- } else
+ } else {
bp->dbg_lnum = 0;
+ }
/* Find the function or file name. Don't accept a function name with (). */
if ((!here && *p == NUL)
@@ -563,13 +685,14 @@ void ex_breakdel(exarg_T *eap)
}
if (ascii_isdigit(*eap->arg)) {
- /* ":breakdel {nr}" */
- nr = atol((char *)eap->arg);
- for (int i = 0; i < gap->ga_len; ++i)
+ // ":breakdel {nr}"
+ nr = atoi((char *)eap->arg);
+ for (int i = 0; i < gap->ga_len; ++i) {
if (DEBUGGY(gap, i).dbg_nr == nr) {
todel = i;
break;
}
+ }
} else if (*eap->arg == '*') {
todel = 0;
del_all = TRUE;
@@ -602,11 +725,13 @@ void ex_breakdel(exarg_T *eap)
--gap->ga_len;
if (todel < gap->ga_len)
memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
- (gap->ga_len - todel) * sizeof(struct debuggy));
- if (eap->cmdidx == CMD_breakdel)
+ (size_t)(gap->ga_len - todel) * sizeof(struct debuggy));
+ if (eap->cmdidx == CMD_breakdel) {
++debug_tick;
- if (!del_all)
+ }
+ if (!del_all) {
break;
+ }
}
/* If all breakpoints were removed clear the array. */
@@ -697,14 +822,13 @@ debuggy_find (
/* Skip entries that are not useful or are for a line that is beyond
* an already found breakpoint. */
bp = &DEBUGGY(gap, i);
- if (((bp->dbg_type == DBG_FILE) == file && (
- gap == &prof_ga ||
- (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum))))) {
- /*
- * Save the value of got_int and reset it. We don't want a
- * previous interruption cancel matching, only hitting CTRL-C
- * while matching should abort it.
- */
+ if (((bp->dbg_type == DBG_FILE) == file
+ && (gap == &prof_ga
+ || (bp->dbg_lnum > after
+ && (lnum == 0 || bp->dbg_lnum < lnum))))) {
+ // Save the value of got_int and reset it. We don't want a
+ // previous interruption cancel matching, only hitting CTRL-C
+ // while matching should abort it.
prev_got_int = got_int;
got_int = FALSE;
if (vim_regexec_prog(&bp->dbg_prog, false, name, (colnr_T)0)) {
@@ -810,8 +934,8 @@ void ex_pydo3(exarg_T *eap)
/* Command line expansion for :profile. */
static enum {
- PEXP_SUBCMD, /* expand :profile sub-commands */
- PEXP_FUNC /* expand :profile func {funcname} */
+ PEXP_SUBCMD, ///< expand :profile sub-commands
+ PEXP_FUNC ///< expand :profile func {funcname}
} pexpand_what;
static char *pexpand_cmds[] = {
@@ -892,7 +1016,7 @@ static void profile_reset(void)
for (int id = 1; id <= script_items.ga_len; id++) {
scriptitem_T *si = &SCRIPT_ITEM(id);
if (si->sn_prof_on) {
- si->sn_prof_on = 0;
+ si->sn_prof_on = false;
si->sn_pr_force = 0;
si->sn_pr_child = profile_zero();
si->sn_pr_nest = 0;
@@ -949,7 +1073,7 @@ static void profile_init(scriptitem_T *si)
ga_init(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
si->sn_prl_idx = -1;
- si->sn_prof_on = TRUE;
+ si->sn_prof_on = true;
si->sn_pr_nest = 0;
}
@@ -1242,28 +1366,31 @@ static void add_bufnum(int *bufnrs, int *bufnump, int nr)
*bufnump = *bufnump + 1;
}
-/*
- * Return TRUE if any buffer was changed and cannot be abandoned.
- * That changed buffer becomes the current buffer.
- */
-int
-check_changed_any (
- int hidden /* Only check hidden buffers */
-)
+/// Check if any buffer was changed and cannot be abandoned.
+/// That changed buffer becomes the current buffer.
+/// When "unload" is true the current buffer is unloaded instead of making it
+/// hidden. This is used for ":q!".
+///
+/// @param[in] hidden specifies whether to check only hidden buffers.
+/// @param[in] unload specifies whether to unload, instead of hide, the buffer.
+///
+/// @returns true if any buffer is changed and cannot be abandoned
+int check_changed_any(bool hidden, bool unload)
{
- int ret = FALSE;
+ bool ret = false;
int save;
int i;
int bufnum = 0;
- int bufcount = 0;
+ size_t bufcount = 0;
int *bufnrs;
FOR_ALL_BUFFERS(buf) {
++bufcount;
}
- if (bufcount == 0)
- return FALSE;
+ if (bufcount == 0) {
+ return false;
+ }
bufnrs = xmalloc(sizeof(*bufnrs) * bufcount);
@@ -1347,9 +1474,10 @@ check_changed_any (
}
buf_found:
- /* Open the changed buffer in the current window. */
- if (buf != curbuf)
- set_curbuf(buf, DOBUF_GOTO);
+ // Open the changed buffer in the current window.
+ if (buf != curbuf) {
+ set_curbuf(buf, unload ? DOBUF_UNLOAD : DOBUF_GOTO);
+ }
theend:
xfree(bufnrs);
@@ -1488,9 +1616,15 @@ do_arglist (
char_u *p;
int match;
- /*
- * Collect all file name arguments in "new_ga".
- */
+ // Set default argument for ":argadd" command.
+ if (what == AL_ADD && *str == NUL) {
+ if (curbuf->b_ffname == NULL) {
+ return FAIL;
+ }
+ str = curbuf->b_fname;
+ }
+
+ // Collect all file name arguments in "new_ga".
get_arglist(&new_ga, str);
if (what == AL_DEL) {
@@ -1520,7 +1654,7 @@ do_arglist (
didone = TRUE;
xfree(ARGLIST[match].ae_fname);
memmove(ARGLIST + match, ARGLIST + match + 1,
- (ARGCOUNT - match - 1) * sizeof(aentry_T));
+ (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T));
--ALIST(curwin)->al_ga.ga_len;
if (curwin->w_arg_idx > match)
--curwin->w_arg_idx;
@@ -1537,9 +1671,7 @@ do_arglist (
int i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
&exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
ga_clear(&new_ga);
- if (i == FAIL)
- return FAIL;
- if (exp_count == 0) {
+ if (i == FAIL || exp_count == 0) {
EMSG(_(e_nomatch));
return FAIL;
}
@@ -1702,10 +1834,11 @@ void ex_argument(exarg_T *eap)
{
int i;
- if (eap->addr_count > 0)
- i = eap->line2 - 1;
- else
+ if (eap->addr_count > 0) {
+ i = (int)eap->line2 - 1;
+ } else {
i = curwin->w_arg_idx;
+ }
do_argfile(eap, i);
}
@@ -1844,27 +1977,36 @@ void ex_argadd(exarg_T *eap)
void ex_argdelete(exarg_T *eap)
{
if (eap->addr_count > 0) {
- /* ":1,4argdel": Delete all arguments in the range. */
- if (eap->line2 > ARGCOUNT)
+ // ":1,4argdel": Delete all arguments in the range.
+ if (eap->line2 > ARGCOUNT) {
eap->line2 = ARGCOUNT;
- int n = eap->line2 - eap->line1 + 1;
- if (*eap->arg != NUL || n <= 0)
+ }
+ linenr_T n = eap->line2 - eap->line1 + 1;
+ if (*eap->arg != NUL || n <= 0) {
EMSG(_(e_invarg));
- else {
- for (int i = eap->line1; i <= eap->line2; ++i)
+ } else {
+ for (linenr_T i = eap->line1; i <= eap->line2; ++i) {
xfree(ARGLIST[i - 1].ae_fname);
+ }
memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
- (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
- ALIST(curwin)->al_ga.ga_len -= n;
- if (curwin->w_arg_idx >= eap->line2)
- curwin->w_arg_idx -= n;
- else if (curwin->w_arg_idx > eap->line1)
- curwin->w_arg_idx = eap->line1;
- }
- } else if (*eap->arg == NUL)
+ (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T));
+ ALIST(curwin)->al_ga.ga_len -= (int)n;
+ if (curwin->w_arg_idx >= eap->line2) {
+ curwin->w_arg_idx -= (int)n;
+ } else if (curwin->w_arg_idx > eap->line1) {
+ curwin->w_arg_idx = (int)eap->line1;
+ }
+ if (ARGCOUNT == 0) {
+ curwin->w_arg_idx = 0;
+ } else if (curwin->w_arg_idx >= ARGCOUNT) {
+ curwin->w_arg_idx = ARGCOUNT - 1;
+ }
+ }
+ } else if (*eap->arg == NUL) {
EMSG(_(e_argreq));
- else
+ } else {
do_arglist(eap->arg, AL_DEL, 0);
+ }
maketitle();
}
@@ -1909,7 +2051,7 @@ void ex_listdo(exarg_T *eap)
}
break;
case CMD_argdo:
- i = eap->line1 - 1;
+ i = (int)eap->line1 - 1;
break;
default:
break;
@@ -1932,8 +2074,8 @@ void ex_listdo(exarg_T *eap)
if (buf != NULL) {
goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum);
}
- } else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
- eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
+ } else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
+ || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
qf_size = qf_get_size(eap);
assert(eap->line1 >= 0);
if (qf_size == 0 || (size_t)eap->line1 > qf_size) {
@@ -1942,10 +2084,11 @@ void ex_listdo(exarg_T *eap)
ex_cc(eap);
buf = curbuf;
- i = eap->line1 - 1;
+ i = (int)eap->line1 - 1;
if (eap->addr_count <= 0) {
// Default to all quickfix/location list entries.
- eap->line2 = qf_size;
+ assert(qf_size < MAXLNUM);
+ eap->line2 = (linenr_T)qf_size;
}
}
} else {
@@ -2034,8 +2177,8 @@ void ex_listdo(exarg_T *eap)
}
}
- if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
- eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
+ if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
+ || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
assert(i >= 0);
if ((size_t)i >= qf_size || i >= eap->line2) {
break;
@@ -2090,6 +2233,7 @@ alist_add_list (
int after /* where to add: 0 = before first one */
)
{
+ int old_argcount = ARGCOUNT;
ga_grow(&ALIST(curwin)->al_ga, count);
{
if (after < 0)
@@ -2098,14 +2242,15 @@ alist_add_list (
after = ARGCOUNT;
if (after < ARGCOUNT)
memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
- (ARGCOUNT - after) * sizeof(aentry_T));
+ (size_t)(ARGCOUNT - after) * sizeof(aentry_T));
for (int i = 0; i < count; ++i) {
ARGLIST[after + i].ae_fname = files[i];
ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED);
}
ALIST(curwin)->al_ga.ga_len += count;
- if (curwin->w_arg_idx >= after)
- ++curwin->w_arg_idx;
+ if (old_argcount > 0 && curwin->w_arg_idx >= after) {
+ curwin->w_arg_idx += count;
+ }
return after;
}
}
@@ -2350,31 +2495,31 @@ int source_level(void *cookie)
return ((struct source_cookie *)cookie)->level;
}
-
-#if (defined(WIN32) && defined(FEAT_CSCOPE)) || defined(HAVE_FD_CLOEXEC)
-# define USE_FOPEN_NOINH
-/*
- * Special function to open a file without handle inheritance.
- * When possible the handle is closed on exec().
- */
+/// Special function to open a file without handle inheritance.
+/// If possible the handle is closed on exec().
static FILE *fopen_noinh_readbin(char *filename)
{
+#ifdef WIN32
+ int fd_tmp = os_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
+#else
int fd_tmp = os_open(filename, O_RDONLY, 0);
+#endif
- if (fd_tmp < 0)
+ if (fd_tmp < 0) {
return NULL;
+ }
-# ifdef HAVE_FD_CLOEXEC
+#ifdef HAVE_FD_CLOEXEC
{
int fdflags = fcntl(fd_tmp, F_GETFD);
- if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
- fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC);
+ if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) {
+ (void)fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC);
+ }
}
-# endif
+#endif
return fdopen(fd_tmp, READBIN);
}
-#endif
/*
@@ -2428,11 +2573,7 @@ do_source (
/* Apply SourcePre autocommands, they may get the file. */
apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf);
-#ifdef USE_FOPEN_NOINH
cookie.fp = fopen_noinh_readbin((char *)fname_exp);
-#else
- cookie.fp = mch_fopen((char *)fname_exp, READBIN);
-#endif
if (cookie.fp == NULL && check_other) {
/*
* Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
@@ -2441,15 +2582,8 @@ do_source (
p = path_tail(fname_exp);
if ((*p == '.' || *p == '_')
&& (STRICMP(p + 1, "nvimrc") == 0 || STRICMP(p + 1, "exrc") == 0)) {
- if (*p == '_')
- *p = '.';
- else
- *p = '_';
-#ifdef USE_FOPEN_NOINH
+ *p = (*p == '_') ? '.' : '_';
cookie.fp = fopen_noinh_readbin((char *)fname_exp);
-#else
- cookie.fp = mch_fopen((char *)fname_exp, READBIN);
-#endif
}
}
@@ -2571,7 +2705,7 @@ do_source (
while (script_items.ga_len < current_SID) {
++script_items.ga_len;
SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
- SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE;
+ SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false;
}
si = &SCRIPT_ITEM(current_SID);
si->sn_name = fname_exp;
@@ -3162,27 +3296,30 @@ static char_u *get_mess_env(void)
*/
void set_lang_var(void)
{
- char_u *loc;
+ const char *loc;
# ifdef HAVE_GET_LOCALE_VAL
- loc = (char_u *)get_locale_val(LC_CTYPE);
+ loc = get_locale_val(LC_CTYPE);
# else
- /* setlocale() not supported: use the default value */
- loc = (char_u *)"C";
+ // setlocale() not supported: use the default value
+ loc = "C";
# endif
set_vim_var_string(VV_CTYPE, loc, -1);
/* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
* back to LC_CTYPE if it's empty. */
# ifdef HAVE_WORKING_LIBINTL
- loc = get_mess_env();
+ loc = (char *) get_mess_env();
+# elif defined(LC_MESSAGES)
+ loc = get_locale_val(LC_MESSAGES);
# else
- loc = (char_u *)get_locale_val(LC_MESSAGES);
+ // In Windows LC_MESSAGES is not defined fallback to LC_CTYPE
+ loc = get_locale_val(LC_CTYPE);
# endif
set_vim_var_string(VV_LANG, loc, -1);
# ifdef HAVE_GET_LOCALE_VAL
- loc = (char_u *)get_locale_val(LC_TIME);
+ loc = get_locale_val(LC_TIME);
# endif
set_vim_var_string(VV_LC_TIME, loc, -1);
}
@@ -3386,8 +3523,8 @@ static void script_host_execute(char *name, exarg_T *eap)
// script
list_append_string(args, script ? script : eap->arg, -1);
// current range
- list_append_number(args, eap->line1);
- list_append_number(args, eap->line2);
+ list_append_number(args, (int)eap->line1);
+ list_append_number(args, (int)eap->line2);
(void)eval_call_provider(name, "execute", args);
}
@@ -3403,16 +3540,16 @@ static void script_host_execute_file(char *name, exarg_T *eap)
// filename
list_append_string(args, buffer, -1);
// current range
- list_append_number(args, eap->line1);
- list_append_number(args, eap->line2);
+ list_append_number(args, (int)eap->line1);
+ list_append_number(args, (int)eap->line2);
(void)eval_call_provider(name, "execute_file", args);
}
static void script_host_do_range(char *name, exarg_T *eap)
{
list_T *args = list_alloc();
- list_append_number(args, eap->line1);
- list_append_number(args, eap->line2);
+ list_append_number(args, (int)eap->line1);
+ list_append_number(args, (int)eap->line2);
list_append_string(args, eap->arg, -1);
(void)eval_call_provider(name, "do_range", args);
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 28ff6fded4..59962c153b 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -381,15 +381,14 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
suppress_errthrow = FALSE;
}
- /*
- * If requested, store and reset the global values controlling the
- * exception handling (used when debugging). Otherwise clear it to avoid
- * a bogus compiler warning when the optimizer uses inline functions...
- */
- if (flags & DOCMD_EXCRESET)
+ // If requested, store and reset the global values controlling the
+ // exception handling (used when debugging). Otherwise clear it to avoid
+ // a bogus compiler warning when the optimizer uses inline functions...
+ if (flags & DOCMD_EXCRESET) {
save_dbg_stuff(&debug_saved);
- else
- memset(&debug_saved, 0, 1);
+ } else {
+ memset(&debug_saved, 0, sizeof(debug_saved));
+ }
initial_trylevel = trylevel;
@@ -1655,16 +1654,15 @@ static char_u * do_one_cmd(char_u **cmdlinep,
* If we got a line, but no command, then go to the line.
* If we find a '|' or '\n' we set ea.nextcmd.
*/
- if (*ea.cmd == NUL || *ea.cmd == '"' ||
- (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) {
- /*
- * strange vi behaviour:
- * ":3" jumps to line 3
- * ":3|..." prints line 3
- * ":|" prints current line
- */
- if (ea.skip) /* skip this if inside :if */
+ if (*ea.cmd == NUL || *ea.cmd == '"'
+ || (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) {
+ // strange vi behaviour:
+ // ":3" jumps to line 3
+ // ":3|..." prints line 3
+ // ":|" prints current line
+ if (ea.skip) { // skip this if inside :if
goto doend;
+ }
if (*ea.cmd == '|' || (exmode_active && ea.line1 != ea.line2)) {
ea.cmdidx = CMD_print;
ea.argt = RANGE | COUNT | TRLBAR;
@@ -1702,9 +1700,9 @@ static char_u * do_one_cmd(char_u **cmdlinep,
p = vim_strnsave(ea.cmd, p - ea.cmd);
int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, TRUE, NULL);
xfree(p);
- if (ret && !aborting()) {
- p = find_command(&ea, NULL);
- }
+ // If the autocommands did something and didn't cause an error, try
+ // finding the command again.
+ p = (ret && !aborting()) ? find_command(&ea, NULL) : NULL;
}
if (p == NULL) {
@@ -1827,9 +1825,10 @@ static char_u * do_one_cmd(char_u **cmdlinep,
correct_range(&ea);
- if (((ea.argt & WHOLEFOLD) || ea.addr_count >= 2) && !global_busy) {
- /* Put the first line at the start of a closed fold, put the last line
- * at the end of a closed fold. */
+ if (((ea.argt & WHOLEFOLD) || ea.addr_count >= 2) && !global_busy
+ && ea.addr_type == ADDR_LINES) {
+ // Put the first line at the start of a closed fold, put the last line
+ // at the end of a closed fold.
(void)hasFolding(ea.line1, &ea.line1, NULL);
(void)hasFolding(ea.line2, NULL, &ea.line2);
}
@@ -2348,8 +2347,11 @@ static char_u *find_command(exarg_T *eap, int *full)
eap->cmdidx = CMD_k;
++p;
} else if (p[0] == 's'
- && ((p[1] == 'c' && p[2] != 's' && p[2] != 'r'
- && p[3] != 'i' && p[4] != 'p')
+ && ((p[1] == 'c'
+ && (p[2] == NUL
+ || (p[2] != 's' && p[2] != 'r'
+ && (p[3] == NUL
+ || (p[3] != 'i' && p[4] != 'p')))))
|| p[1] == 'g'
|| (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g')
|| p[1] == 'I'
@@ -2676,16 +2678,25 @@ set_one_cmd_context (
p = cmd + 1;
} else {
p = cmd;
- while (ASCII_ISALPHA(*p) || *p == '*') /* Allow * wild card */
- ++p;
- /* check for non-alpha command */
- if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL)
- ++p;
- /* for python 3.x: ":py3*" commands completion */
+ while (ASCII_ISALPHA(*p) || *p == '*') { // Allow * wild card
+ p++;
+ }
+ // a user command may contain digits
+ if (ASCII_ISUPPER(cmd[0])) {
+ while (ASCII_ISALNUM(*p) || *p == '*') {
+ p++;
+ }
+ }
+ // for python 3.x: ":py3*" commands completion
if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3') {
- ++p;
- while (ASCII_ISALPHA(*p) || *p == '*')
- ++p;
+ p++;
+ while (ASCII_ISALPHA(*p) || *p == '*') {
+ p++;
+ }
+ }
+ // check for non-alpha command
+ if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) {
+ p++;
}
len = (int)(p - cmd);
@@ -2699,9 +2710,11 @@ set_one_cmd_context (
(size_t)len) == 0)
break;
- if (cmd[0] >= 'A' && cmd[0] <= 'Z')
- while (ASCII_ISALNUM(*p) || *p == '*') /* Allow * wild card */
- ++p;
+ if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
+ while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card
+ p++;
+ }
+ }
}
/*
@@ -2810,10 +2823,11 @@ set_one_cmd_context (
}
}
- /* no arguments allowed */
- if (!(ea.argt & EXTRA) && *arg != NUL &&
- vim_strchr((char_u *)"|\"", *arg) == NULL)
+ // no arguments allowed
+ if (!(ea.argt & EXTRA) && *arg != NUL
+ && vim_strchr((char_u *)"|\"", *arg) == NULL) {
return NULL;
+ }
/* Find start of last argument (argument just before cursor): */
p = buff;
@@ -2945,8 +2959,11 @@ set_one_cmd_context (
case CMD_chdir:
case CMD_lcd:
case CMD_lchdir:
- if (xp->xp_context == EXPAND_FILES)
+ case CMD_tcd:
+ case CMD_tchdir:
+ if (xp->xp_context == EXPAND_FILES) {
xp->xp_context = EXPAND_DIRECTORIES;
+ }
break;
case CMD_help:
xp->xp_context = EXPAND_HELP;
@@ -4307,7 +4324,7 @@ static void ex_unmap(exarg_T *eap)
*/
static void ex_mapclear(exarg_T *eap)
{
- map_clear(eap->cmd, eap->arg, eap->forceit, FALSE);
+ map_clear_mode(eap->cmd, eap->arg, eap->forceit, false);
}
/*
@@ -4315,7 +4332,7 @@ static void ex_mapclear(exarg_T *eap)
*/
static void ex_abclear(exarg_T *eap)
{
- map_clear(eap->cmd, eap->arg, TRUE, TRUE);
+ map_clear_mode(eap->cmd, eap->arg, true, true);
}
static void ex_autocmd(exarg_T *eap)
@@ -4540,7 +4557,8 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep,
char_u *rep_buf = NULL;
garray_T *gap;
- replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE);
+ replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, false,
+ CPO_TO_CPO_FLAGS);
if (rep_buf == NULL) {
/* Can't replace termcodes - try using the string as is */
rep_buf = vim_strsave(rep);
@@ -4754,14 +4772,15 @@ static void uc_list(char_u *name, size_t name_len)
IObuff[len++] = ' ';
} while (len < 11);
- /* Address Type */
- for (j = 0; addr_type_complete[j].expand != -1; ++j)
- if (addr_type_complete[j].expand != ADDR_LINES &&
- addr_type_complete[j].expand == cmd->uc_addr_type) {
+ // Address Type
+ for (j = 0; addr_type_complete[j].expand != -1; j++) {
+ if (addr_type_complete[j].expand != ADDR_LINES
+ && addr_type_complete[j].expand == cmd->uc_addr_type) {
STRCPY(IObuff + len, addr_type_complete[j].name);
len += (int)STRLEN(IObuff + len);
break;
}
+ }
do {
IObuff[len++] = ' ';
@@ -5486,7 +5505,8 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt,
int *addr_type_arg)
{
int i, a, b;
- for (i = 0; addr_type_complete[i].expand != -1; ++i) {
+
+ for (i = 0; addr_type_complete[i].expand != -1; i++) {
a = (int)STRLEN(addr_type_complete[i].name) == vallen;
b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0;
if (a && b) {
@@ -5497,8 +5517,8 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt,
if (addr_type_complete[i].expand == -1) {
char_u *err = value;
- for (i = 0; err[i] == NUL || !ascii_iswhite(err[i]); i++)
- ;
+
+ for (i = 0; err[i] != NUL && !ascii_iswhite(err[i]); i++) {}
err[i] = NUL;
EMSG2(_("E180: Invalid address type value: %s"), err);
return FAIL;
@@ -5635,12 +5655,13 @@ static void ex_quit(exarg_T *eap)
wp = curwin;
}
- apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf);
- /* Refuse to quit when locked or when the buffer in the last window is
- * being closed (can only happen in autocommands). */
- if (curbuf_locked() ||
- (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_closing))
+ apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, curbuf);
+ // Refuse to quit when locked or when the buffer in the last window is
+ // being closed (can only happen in autocommands).
+ if (curbuf_locked()
+ || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_closing)) {
return;
+ }
/*
@@ -5650,10 +5671,10 @@ static void ex_quit(exarg_T *eap)
exiting = TRUE;
if ((!P_HID(curbuf)
&& check_changed(curbuf, (p_awa ? CCGD_AW : 0)
- | (eap->forceit ? CCGD_FORCEIT : 0)
- | CCGD_EXCMD))
- || check_more(TRUE, eap->forceit) == FAIL
- || (only_one_window() && check_changed_any(eap->forceit))) {
+ | (eap->forceit ? CCGD_FORCEIT : 0)
+ | CCGD_EXCMD))
+ || check_more(true, eap->forceit) == FAIL
+ || (only_one_window() && check_changed_any(eap->forceit, true))) {
not_exiting();
} else {
// quit last window
@@ -5702,9 +5723,10 @@ static void ex_quit_all(exarg_T *eap)
if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing))
return;
- exiting = TRUE;
- if (eap->forceit || !check_changed_any(FALSE))
+ exiting = true;
+ if (eap->forceit || !check_changed_any(false, false)) {
getout(0);
+ }
not_exiting();
}
@@ -5989,21 +6011,22 @@ static void ex_exit(exarg_T *eap)
if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing))
return;
- /*
- * if more files or windows we won't exit
- */
- if (check_more(FALSE, eap->forceit) == OK && only_one_window())
- exiting = TRUE;
- if ( ((eap->cmdidx == CMD_wq
- || curbufIsChanged())
- && do_write(eap) == FAIL)
- || check_more(TRUE, eap->forceit) == FAIL
- || (only_one_window() && check_changed_any(eap->forceit))) {
+ // if more files or windows we won't exit
+ if (check_more(false, eap->forceit) == OK && only_one_window()) {
+ exiting = true;
+ }
+ if (((eap->cmdidx == CMD_wq
+ || curbufIsChanged())
+ && do_write(eap) == FAIL)
+ || check_more(true, eap->forceit) == FAIL
+ || (only_one_window() && check_changed_any(eap->forceit, false))) {
not_exiting();
} else {
- if (only_one_window()) /* quit last window, exit Vim */
+ if (only_one_window()) {
+ // quit last window, exit Vim
getout(0);
- /* Quit current window, may free the buffer. */
+ }
+ // Quit current window, may free the buffer.
win_close(curwin, !P_HID(curwin->w_buffer));
}
}
@@ -6277,10 +6300,8 @@ void ex_splitview(exarg_T *eap)
if (eap->cmdidx == CMD_tabedit
|| eap->cmdidx == CMD_tabfind
|| eap->cmdidx == CMD_tabnew) {
- if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab
- : eap->addr_count == 0 ? 0
- : (int)eap->line2 + 1) != FAIL) {
- apply_autocmds(EVENT_TABNEW, eap->arg, eap->arg, FALSE, curbuf);
+ if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab : eap->addr_count == 0
+ ? 0 : (int)eap->line2 + 1, eap->arg) != FAIL) {
do_exedit(eap, old_curwin);
apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, FALSE, curbuf);
@@ -6803,36 +6824,55 @@ void free_cd_dir(void)
#endif
-/*
- * Deal with the side effects of changing the current directory.
- * When "local" is TRUE then this was after an ":lcd" command.
- */
-void post_chdir(int local)
+/// Deal with the side effects of changing the current directory.
+///
+/// @param scope Scope of the function call (global, tab or window).
+void post_chdir(CdScope scope)
{
+ // The local directory of the current window is always overwritten.
xfree(curwin->w_localdir);
curwin->w_localdir = NULL;
- if (local) {
- /* If still in global directory, need to remember current
- * directory as global directory. */
- if (globaldir == NULL && prev_dir != NULL)
+
+ // Overwrite the local directory of the current tab page for `cd` and `tcd`
+ if (scope >= kCdScopeTab) {
+ xfree(curtab->localdir);
+ curtab->localdir = NULL;
+ }
+
+ if (scope < kCdScopeGlobal) {
+ // If still in global directory, need to remember current directory as
+ // global directory.
+ if (globaldir == NULL && prev_dir != NULL) {
globaldir = vim_strsave(prev_dir);
- /* Remember this local directory for the window. */
- if (os_dirname(NameBuff, MAXPATHL) == OK)
- curwin->w_localdir = vim_strsave(NameBuff);
- } else {
- /* We are now in the global directory, no need to remember its
- * name. */
+ }
+ }
+
+ switch (scope) {
+ case kCdScopeGlobal:
+ // We are now in the global directory, no need to remember its name.
xfree(globaldir);
globaldir = NULL;
+ break;
+ case kCdScopeTab:
+ // Remember this local directory for the tab page.
+ if (os_dirname(NameBuff, MAXPATHL) == OK) {
+ curtab->localdir = vim_strsave(NameBuff);
+ }
+ break;
+ case kCdScopeWindow:
+ // Remember this local directory for the window.
+ if (os_dirname(NameBuff, MAXPATHL) == OK) {
+ curwin->w_localdir = vim_strsave(NameBuff);
+ }
+ break;
}
shorten_fnames(TRUE);
}
-/*
- * ":cd", ":lcd", ":chdir" and ":lchdir".
- */
+
+/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`.
void ex_cd(exarg_T *eap)
{
char_u *new_dir;
@@ -6873,10 +6913,25 @@ void ex_cd(exarg_T *eap)
new_dir = NameBuff;
}
#endif
- if (new_dir == NULL || vim_chdir(new_dir))
+ if (vim_chdir(new_dir)) {
EMSG(_(e_failed));
- else {
- post_chdir(eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir);
+ } else {
+ CdScope scope = kCdScopeGlobal; // Depends on command invoked
+
+ switch (eap->cmdidx) {
+ case CMD_tcd:
+ case CMD_tchdir:
+ scope = kCdScopeTab;
+ break;
+ case CMD_lcd:
+ case CMD_lchdir:
+ scope = kCdScopeWindow;
+ break;
+ default:
+ break;
+ }
+
+ post_chdir(scope);
/* Echo the new current directory if the command was typed. */
if (KeyTyped || p_verbose >= 5)
@@ -6934,10 +6989,10 @@ static void ex_sleep(exarg_T *eap)
*/
void do_sleep(long msec)
{
- long done;
ui_flush(); // flush before waiting
- for (done = 0; !got_int && done < msec; done += 1000L) {
- os_delay(msec - done > 1000L ? 1000L : msec - done, true);
+ for (long left = msec; !got_int && left > 0; left -= 1000L) {
+ int next = left > 1000l ? 1000 : (int)left;
+ LOOP_PROCESS_EVENTS_UNTIL(&loop, loop.events, (int)next, got_int);
os_breakcheck();
}
}
@@ -7020,9 +7075,9 @@ static void ex_operators(exarg_T *eap)
oa.start.lnum = eap->line1;
oa.end.lnum = eap->line2;
oa.line_count = eap->line2 - eap->line1 + 1;
- oa.motion_type = MLINE;
- virtual_op = FALSE;
- if (eap->cmdidx != CMD_yank) { /* position cursor for undo */
+ oa.motion_type = kMTLineWise;
+ virtual_op = false;
+ if (eap->cmdidx != CMD_yank) { // position cursor for undo
setpcmark();
curwin->w_cursor.lnum = eap->line1;
beginline(BL_SOL | BL_FIX);
@@ -7410,10 +7465,10 @@ static int mksession_nl = FALSE; /* use NL only in put_eol() */
static void ex_mkrc(exarg_T *eap)
{
FILE *fd;
- int failed = FALSE;
- int view_session = FALSE;
- int using_vdir = FALSE; /* using 'viewdir'? */
- char_u *viewFile = NULL;
+ int failed = false;
+ int view_session = false;
+ int using_vdir = false; // using 'viewdir'?
+ char *viewFile = NULL;
unsigned *flagp;
if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview) {
@@ -7424,32 +7479,34 @@ static void ex_mkrc(exarg_T *eap)
* short file name when 'acd' is set, that is checked later. */
did_lcd = FALSE;
- char_u *fname;
- /* ":mkview" or ":mkview 9": generate file name with 'viewdir' */
+ char *fname;
+ // ":mkview" or ":mkview 9": generate file name with 'viewdir'
if (eap->cmdidx == CMD_mkview
&& (*eap->arg == NUL
|| (ascii_isdigit(*eap->arg) && eap->arg[1] == NUL))) {
- eap->forceit = TRUE;
- fname = (char_u *)get_view_file(*eap->arg);
- if (fname == NULL)
+ eap->forceit = true;
+ fname = get_view_file(*eap->arg);
+ if (fname == NULL) {
return;
+ }
viewFile = fname;
- using_vdir = TRUE;
- } else if (*eap->arg != NUL)
- fname = eap->arg;
- else if (eap->cmdidx == CMD_mkvimrc)
- fname = (char_u *)VIMRC_FILE;
- else if (eap->cmdidx == CMD_mksession)
- fname = (char_u *)SESSION_FILE;
- else
- fname = (char_u *)EXRC_FILE;
+ using_vdir = true;
+ } else if (*eap->arg != NUL) {
+ fname = (char *) eap->arg;
+ } else if (eap->cmdidx == CMD_mkvimrc) {
+ fname = VIMRC_FILE;
+ } else if (eap->cmdidx == CMD_mksession) {
+ fname = SESSION_FILE;
+ } else {
+ fname = EXRC_FILE;
+ }
/* When using 'viewdir' may have to create the directory. */
if (using_vdir && !os_isdir(p_vdir)) {
vim_mkdir_emsg(p_vdir, 0755);
}
- fd = open_exfile(fname, eap->forceit, WRITEBIN);
+ fd = open_exfile((char_u *) fname, eap->forceit, WRITEBIN);
if (fd != NULL) {
if (eap->cmdidx == CMD_mkview)
flagp = &vop_flags;
@@ -7493,8 +7550,9 @@ static void ex_mkrc(exarg_T *eap)
|| os_chdir((char *)dirnow) != 0)
*dirnow = NUL;
if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) {
- if (vim_chdirfile(fname) == OK)
- shorten_fnames(TRUE);
+ if (vim_chdirfile((char_u *) fname) == OK) {
+ shorten_fnames(true);
+ }
} else if (*dirnow != NUL
&& (ssop_flags & SSOP_CURDIR) && globaldir != NULL) {
if (os_chdir((char *)globaldir) == 0)
@@ -7539,15 +7597,14 @@ static void ex_mkrc(exarg_T *eap)
failed |= fclose(fd);
- if (failed)
+ if (failed) {
EMSG(_(e_write));
- else if (eap->cmdidx == CMD_mksession) {
- /* successful session write - set this_session var */
- char_u *tbuf;
-
- tbuf = xmalloc(MAXPATHL);
- if (vim_FullName((char *)fname, (char *)tbuf, MAXPATHL, FALSE) == OK)
+ } else if (eap->cmdidx == CMD_mksession) {
+ // successful session write - set this_session var
+ char *const tbuf = xmalloc(MAXPATHL);
+ if (vim_FullName(fname, tbuf, MAXPATHL, false) == OK) {
set_vim_var_string(VV_THIS_SESSION, tbuf, -1);
+ }
xfree(tbuf);
}
#ifdef MKSESSION_NL
@@ -8280,16 +8337,22 @@ static char_u *arg_all(void)
retval[len] = ' ';
++len;
}
- for (; *p != NUL; ++p) {
- if (*p == ' ' || *p == '\\') {
- /* insert a backslash */
- if (retval != NULL)
+ for (; *p != NUL; p++) {
+ if (*p == ' '
+#ifndef BACKSLASH_IN_FILENAME
+ || *p == '\\'
+#endif
+ ) {
+ // insert a backslash
+ if (retval != NULL) {
retval[len] = '\\';
- ++len;
+ }
+ len++;
}
- if (retval != NULL)
+ if (retval != NULL) {
retval[len] = *p;
- ++len;
+ }
+ len++;
}
}
@@ -8765,19 +8828,18 @@ static int ses_do_frame(frame_T *fr)
return FALSE;
}
-/*
- * Return non-zero if window "wp" is to be stored in the Session.
- */
+/// Return non-zero if window "wp" is to be stored in the Session.
static int ses_do_win(win_T *wp)
{
if (wp->w_buffer->b_fname == NULL
- /* When 'buftype' is "nofile" can't restore the window contents. */
- || bt_nofile(wp->w_buffer)
- )
+ // When 'buftype' is "nofile" can't restore the window contents.
+ || (!wp->w_buffer->terminal && bt_nofile(wp->w_buffer))) {
return ssop_flags & SSOP_BLANK;
- if (wp->w_buffer->b_help)
+ }
+ if (wp->w_buffer->b_help) {
return ssop_flags & SSOP_HELP;
- return TRUE;
+ }
+ return true;
}
/*
@@ -9127,16 +9189,15 @@ static char *get_view_file(int c)
*/
int put_eol(FILE *fd)
{
- if (
-#ifdef USE_CRNL
- (
-# ifdef MKSESSION_NL
- !mksession_nl &&
-# endif
- (putc('\r', fd) < 0)) ||
+#if defined(USE_CRNL) && defined(MKSESSION_NL)
+ if ((!mksession_nl && putc('\r', fd) < 0) || putc('\n', fd) < 0) {
+#elif defined(USE_CRNL)
+ if (putc('\r', fd) < 0 || putc('\n', fd) < 0) {
+#else
+ if (putc('\n', fd) < 0) {
#endif
- (putc('\n', fd) < 0))
return FAIL;
+ }
return OK;
}
@@ -9213,9 +9274,9 @@ char_u *get_behave_arg(expand_T *xp, int idx)
return NULL;
}
-static int filetype_detect = FALSE;
-static int filetype_plugin = FALSE;
-static int filetype_indent = FALSE;
+static TriState filetype_detect = kNone;
+static TriState filetype_plugin = kNone;
+static TriState filetype_indent = kNone;
/*
* ":filetype [plugin] [indent] {on,off,detect}"
@@ -9229,27 +9290,27 @@ static int filetype_indent = FALSE;
static void ex_filetype(exarg_T *eap)
{
char_u *arg = eap->arg;
- int plugin = FALSE;
- int indent = FALSE;
+ bool plugin = false;
+ bool indent = false;
if (*eap->arg == NUL) {
/* Print current status. */
smsg("filetype detection:%s plugin:%s indent:%s",
- filetype_detect ? "ON" : "OFF",
- filetype_plugin ? (filetype_detect ? "ON" : "(on)") : "OFF",
- filetype_indent ? (filetype_detect ? "ON" : "(on)") : "OFF");
+ filetype_detect == kTrue ? "ON" : "OFF",
+ filetype_plugin == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF", // NOLINT(whitespace/line_length)
+ filetype_indent == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF"); // NOLINT(whitespace/line_length)
return;
}
/* Accept "plugin" and "indent" in any order. */
for (;; ) {
if (STRNCMP(arg, "plugin", 6) == 0) {
- plugin = TRUE;
+ plugin = true;
arg = skipwhite(arg + 6);
continue;
}
if (STRNCMP(arg, "indent", 6) == 0) {
- indent = TRUE;
+ indent = true;
arg = skipwhite(arg + 6);
continue;
}
@@ -9257,15 +9318,15 @@ static void ex_filetype(exarg_T *eap)
}
if (STRCMP(arg, "on") == 0 || STRCMP(arg, "detect") == 0) {
if (*arg == 'o' || !filetype_detect) {
- source_runtime((char_u *)FILETYPE_FILE, TRUE);
- filetype_detect = TRUE;
+ source_runtime((char_u *)FILETYPE_FILE, true);
+ filetype_detect = kTrue;
if (plugin) {
- source_runtime((char_u *)FTPLUGIN_FILE, TRUE);
- filetype_plugin = TRUE;
+ source_runtime((char_u *)FTPLUGIN_FILE, true);
+ filetype_plugin = kTrue;
}
if (indent) {
- source_runtime((char_u *)INDENT_FILE, TRUE);
- filetype_indent = TRUE;
+ source_runtime((char_u *)INDENT_FILE, true);
+ filetype_indent = kTrue;
}
}
if (*arg == 'd') {
@@ -9275,21 +9336,37 @@ static void ex_filetype(exarg_T *eap)
} else if (STRCMP(arg, "off") == 0) {
if (plugin || indent) {
if (plugin) {
- source_runtime((char_u *)FTPLUGOF_FILE, TRUE);
- filetype_plugin = FALSE;
+ source_runtime((char_u *)FTPLUGOF_FILE, true);
+ filetype_plugin = kFalse;
}
if (indent) {
- source_runtime((char_u *)INDOFF_FILE, TRUE);
- filetype_indent = FALSE;
+ source_runtime((char_u *)INDOFF_FILE, true);
+ filetype_indent = kFalse;
}
} else {
- source_runtime((char_u *)FTOFF_FILE, TRUE);
- filetype_detect = FALSE;
+ source_runtime((char_u *)FTOFF_FILE, true);
+ filetype_detect = kFalse;
}
} else
EMSG2(_(e_invarg2), arg);
}
+/// Do ":filetype plugin indent on" if user did not already do some
+/// permutation thereof.
+void filetype_maybe_enable(void)
+{
+ if (filetype_detect == kNone
+ && filetype_plugin == kNone
+ && filetype_indent == kNone) {
+ source_runtime((char_u *)FILETYPE_FILE, true);
+ filetype_detect = kTrue;
+ source_runtime((char_u *)FTPLUGIN_FILE, true);
+ filetype_plugin = kTrue;
+ source_runtime((char_u *)INDENT_FILE, true);
+ filetype_indent = kTrue;
+ }
+}
+
/*
* ":setfiletype {name}"
*/
@@ -9327,59 +9404,62 @@ static void ex_nohlsearch(exarg_T *eap)
redraw_all_later(SOME_VALID);
}
-/*
- * ":[N]match {group} {pattern}"
- * Sets nextcmd to the start of the next command, if any. Also called when
- * skipping commands to find the next command.
- */
+// ":[N]match {group} {pattern}"
+// Sets nextcmd to the start of the next command, if any. Also called when
+// skipping commands to find the next command.
static void ex_match(exarg_T *eap)
{
- char_u *p;
- char_u *g = NULL;
- char_u *end;
+ char_u *p;
+ char_u *g = NULL;
+ char_u *end;
int c;
int id;
- if (eap->line2 <= 3)
+ if (eap->line2 <= 3) {
id = eap->line2;
- else {
+ } else {
EMSG(e_invcmd);
return;
}
- /* First clear any old pattern. */
- if (!eap->skip)
- match_delete(curwin, id, FALSE);
+ // First clear any old pattern.
+ if (!eap->skip) {
+ match_delete(curwin, id, false);
+ }
- if (ends_excmd(*eap->arg))
+ if (ends_excmd(*eap->arg)) {
end = eap->arg;
- else if ((STRNICMP(eap->arg, "none", 4) == 0
- && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4]))))
+ } else if ((STRNICMP(eap->arg, "none", 4) == 0
+ && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) {
end = eap->arg + 4;
- else {
+ } else {
p = skiptowhite(eap->arg);
- if (!eap->skip)
+ if (!eap->skip) {
g = vim_strnsave(eap->arg, (int)(p - eap->arg));
+ }
p = skipwhite(p);
if (*p == NUL) {
- /* There must be two arguments. */
+ // There must be two arguments.
+ xfree(g);
EMSG2(_(e_invarg2), eap->arg);
return;
}
- end = skip_regexp(p + 1, *p, TRUE, NULL);
+ end = skip_regexp(p + 1, *p, true, NULL);
if (!eap->skip) {
if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) {
+ xfree(g);
eap->errmsg = e_trailing;
return;
}
if (*end != *p) {
+ xfree(g);
EMSG2(_(e_invarg2), p);
return;
}
c = *end;
*end = NUL;
- match_add(curwin, g, p + 1, 10, id, NULL);
+ match_add(curwin, g, p + 1, 10, id, NULL, NULL);
xfree(g);
*end = c;
}
@@ -9419,12 +9499,14 @@ static void ex_folddo(exarg_T *eap)
static void ex_terminal(exarg_T *eap)
{
- // We will call termopen() with ['shell'] if not given a {cmd}.
- char *name = (char *)p_sh;
+ char *name = (char *)p_sh; // Default to 'shell' if {cmd} is not given.
+ bool mustfree = false;
char *lquote = "['";
char *rquote = "']";
+
if (*eap->arg != NUL) {
name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\");
+ mustfree = true;
lquote = rquote = "\"";
}
@@ -9434,7 +9516,7 @@ static void ex_terminal(exarg_T *eap)
eap->forceit==TRUE ? "!" : "", lquote, name, rquote);
do_cmdline_cmd(ex_cmd);
- if (name != (char *)p_sh) {
+ if (mustfree) {
xfree(name);
}
}
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index a5a4edbbbf..dbfc64e2f1 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -19,6 +19,20 @@
#define EXMODE_NORMAL 1
#define EXMODE_VIM 2
+/// The scope of a working-directory command like `:cd`.
+///
+/// Scopes are enumerated from lowest to highest. When adding a scope make sure
+/// to update all functions using scopes as well, such as the implementation of
+/// `getcwd()`. When using scopes as limits (e.g. in loops) don't use the scopes
+/// directly, use `MIN_CD_SCOPE` and `MAX_CD_SCOPE` instead.
+typedef enum {
+ kCdScopeWindow, ///< Affects one window.
+ kCdScopeTab, ///< Affects one tab page.
+ kCdScopeGlobal, ///< Affects the entire instance of Neovim.
+} CdScope;
+#define MIN_CD_SCOPE kCdScopeWindow
+#define MAX_CD_SCOPE kCdScopeGlobal
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_docmd.h.generated.h"
#endif
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index bf67047ae8..82d4c2b2d5 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -378,7 +378,7 @@ char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should
char_u *p, *val;
if (type == ET_ERROR) {
- *should_free = FALSE;
+ *should_free = true;
mesg = ((struct msglist *)value)->throw_msg;
if (cmdname != NULL && *cmdname != NUL) {
size_t cmdlen = STRLEN(cmdname);
@@ -403,14 +403,15 @@ char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should
&& (p[3] == ':'
|| (ascii_isdigit(p[3])
&& p[4] == ':')))))) {
- if (*p == NUL || p == mesg)
- STRCAT(val, mesg); /* 'E123' missing or at beginning */
- else {
- /* '"filename" E123: message text' */
- if (mesg[0] != '"' || p-2 < &mesg[1] ||
- p[-2] != '"' || p[-1] != ' ')
- /* "E123:" is part of the file name. */
+ if (*p == NUL || p == mesg) {
+ STRCAT(val, mesg); // 'E123' missing or at beginning
+ } else {
+ // '"filename" E123: message text'
+ if (mesg[0] != '"' || p-2 < &mesg[1]
+ || p[-2] != '"' || p[-1] != ' ') {
+ // "E123:" is part of the file name.
continue;
+ }
STRCAT(val, p);
p[-2] = NUL;
@@ -569,17 +570,19 @@ static void catch_exception(except_T *excp)
{
excp->caught = caught_stack;
caught_stack = excp;
- set_vim_var_string(VV_EXCEPTION, excp->value, -1);
+ set_vim_var_string(VV_EXCEPTION, (char *) excp->value, -1);
if (*excp->throw_name != NUL) {
- if (excp->throw_lnum != 0)
+ if (excp->throw_lnum != 0) {
vim_snprintf((char *)IObuff, IOSIZE, _("%s, line %" PRId64),
- excp->throw_name, (int64_t)excp->throw_lnum);
- else
+ excp->throw_name, (int64_t)excp->throw_lnum);
+ } else {
vim_snprintf((char *)IObuff, IOSIZE, "%s", excp->throw_name);
- set_vim_var_string(VV_THROWPOINT, IObuff, -1);
- } else
- /* throw_name not set on an exception from a command that was typed. */
+ }
+ set_vim_var_string(VV_THROWPOINT, (char *) IObuff, -1);
+ } else {
+ // throw_name not set on an exception from a command that was typed.
set_vim_var_string(VV_THROWPOINT, NULL, -1);
+ }
if (p_verbose >= 13 || debug_break_level > 0) {
int save_msg_silent = msg_silent;
@@ -614,20 +617,22 @@ static void finish_exception(except_T *excp)
EMSG(_(e_internal));
caught_stack = caught_stack->caught;
if (caught_stack != NULL) {
- set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1);
+ set_vim_var_string(VV_EXCEPTION, (char *) caught_stack->value, -1);
if (*caught_stack->throw_name != NUL) {
- if (caught_stack->throw_lnum != 0)
+ if (caught_stack->throw_lnum != 0) {
vim_snprintf((char *)IObuff, IOSIZE,
- _("%s, line %" PRId64), caught_stack->throw_name,
- (int64_t)caught_stack->throw_lnum);
- else
+ _("%s, line %" PRId64), caught_stack->throw_name,
+ (int64_t)caught_stack->throw_lnum);
+ } else {
vim_snprintf((char *)IObuff, IOSIZE, "%s",
- caught_stack->throw_name);
- set_vim_var_string(VV_THROWPOINT, IObuff, -1);
- } else
- /* throw_name not set on an exception from a command that was
- * typed. */
+ caught_stack->throw_name);
+ }
+ set_vim_var_string(VV_THROWPOINT, (char *) IObuff, -1);
+ } else {
+ // throw_name not set on an exception from a command that was
+ // typed.
set_vim_var_string(VV_THROWPOINT, NULL, -1);
+ }
} else {
set_vim_var_string(VV_EXCEPTION, NULL, -1);
set_vim_var_string(VV_THROWPOINT, NULL, -1);
@@ -1370,19 +1375,24 @@ void ex_catch(exarg_T *eap)
}
save_cpo = p_cpo;
p_cpo = (char_u *)"";
+ // Disable error messages, it will make current exception
+ // invalid
+ emsg_off++;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
- regmatch.rm_ic = FALSE;
- if (end != NULL)
+ emsg_off--;
+ regmatch.rm_ic = false;
+ if (end != NULL) {
*end = save_char;
+ }
p_cpo = save_cpo;
- if (regmatch.regprog == NULL)
+ if (regmatch.regprog == NULL) {
EMSG2(_(e_invarg2), pat);
- else {
- /*
- * Save the value of got_int and reset it. We don't want
- * a previous interruption cancel matching, only hitting
- * CTRL-C while matching should abort it.
- */
+ } else {
+ //
+ // Save the value of got_int and reset it. We don't want
+ // a previous interruption cancel matching, only hitting
+ // CTRL-C while matching should abort it.
+ //
prev_got_int = got_int;
got_int = FALSE;
caught = vim_regexec_nl(&regmatch, current_exception->value,
@@ -1556,22 +1566,21 @@ void ex_endtry(exarg_T *eap)
void *rettv = NULL;
struct condstack *cstack = eap->cstack;
- if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0)
+ if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) {
eap->errmsg = (char_u *)N_("E602: :endtry without :try");
- else {
- /*
- * Don't do something after an error, interrupt or throw in the try
- * block, catch clause, or finally clause preceding this ":endtry" or
- * when an error or interrupt occurred after a ":continue", ":break",
- * ":return", or ":finish" in a try block or catch clause preceding this
- * ":endtry" or when the try block never got active (because of an
- * inactive surrounding conditional or after an error or interrupt or
- * throw) or when there is a surrounding conditional and it has been
- * made inactive by a ":continue", ":break", ":return", or ":finish" in
- * the finally clause. The latter case need not be tested since then
- * anything pending has already been discarded. */
- skip = did_emsg || got_int || did_throw ||
- !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
+ } else {
+ // Don't do something after an error, interrupt or throw in the try
+ // block, catch clause, or finally clause preceding this ":endtry" or
+ // when an error or interrupt occurred after a ":continue", ":break",
+ // ":return", or ":finish" in a try block or catch clause preceding this
+ // ":endtry" or when the try block never got active (because of an
+ // inactive surrounding conditional or after an error or interrupt or
+ // throw) or when there is a surrounding conditional and it has been
+ // made inactive by a ":continue", ":break", ":return", or ":finish" in
+ // the finally clause. The latter case need not be tested since then
+ // anything pending has already been discarded.
+ skip = (did_emsg || got_int || did_throw
+ || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE));
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
eap->errmsg = get_end_emsg(cstack);
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 96bf2c78d2..db21fddedb 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -289,7 +289,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
if (ccline.cmdbuff != NULL) {
// Put line in history buffer (":" and "=" only when it was typed).
- if (ccline.cmdlen && s->firstc != NUL
+ if (s->histype != HIST_INVALID
+ && ccline.cmdlen
+ && s->firstc != NUL
&& (s->some_key_typed || s->histype == HIST_SEARCH)) {
add_to_history(s->histype, ccline.cmdbuff, true,
s->histype == HIST_SEARCH ? s->firstc : NUL);
@@ -357,6 +359,7 @@ static int command_line_execute(VimState *state, int key)
if (s->c == K_EVENT) {
queue_process_events(loop.events);
+ redrawcmdline();
return 1;
}
@@ -622,8 +625,8 @@ static int command_line_execute(VimState *state, int key)
// CTRL-\ e doesn't work when obtaining an expression, unless it
// is in a mapping.
if (s->c != Ctrl_N && s->c != Ctrl_G && (s->c != 'e'
- || (ccline.cmdfirstc == '=' &&
- KeyTyped))) {
+ || (ccline.cmdfirstc == '='
+ && KeyTyped))) {
vungetc(s->c);
s->c = Ctrl_BSL;
} else if (s->c == 'e') {
@@ -1130,7 +1133,7 @@ static int command_line_handle_key(CommandLineState *s)
if (!mouse_has(MOUSE_COMMAND)) {
return command_line_not_changed(s); // Ignore mouse
}
- cmdline_paste(0, true, true);
+ cmdline_paste(eval_has_provider("clipboard") ? '*' : 0, true, true);
redrawcmd();
return command_line_changed(s);
@@ -1268,7 +1271,7 @@ static int command_line_handle_key(CommandLineState *s)
case K_KPAGEUP:
case K_PAGEDOWN:
case K_KPAGEDOWN:
- if (hislen == 0 || s->firstc == NUL) {
+ if (s->histype == HIST_INVALID || hislen == 0 || s->firstc == NUL) {
// no history
return command_line_not_changed(s);
}
@@ -2424,20 +2427,17 @@ void restore_cmdline_alloc(char_u *p)
xfree(p);
}
-/*
- * paste a yank register into the command line.
- * used by CTRL-R command in command-line mode
- * insert_reg() can't be used here, because special characters from the
- * register contents will be interpreted as commands.
- *
- * return FAIL for failure, OK otherwise
- */
-static int
-cmdline_paste (
- int regname,
- int literally, /* Insert text literally instead of "as typed" */
- int remcr /* remove trailing CR */
-)
+/// Paste a yank register into the command line.
+/// Used by CTRL-R command in command-line mode.
+/// insert_reg() can't be used here, because special characters from the
+/// register contents will be interpreted as commands.
+///
+/// @param regname Register name.
+/// @param literally Insert text literally instead of "as typed".
+/// @param remcr When true, remove trailing CR.
+///
+/// @returns FAIL for failure, OK otherwise
+static bool cmdline_paste(int regname, bool literally, bool remcr)
{
long i;
char_u *arg;
@@ -2957,20 +2957,37 @@ ExpandOne (
}
}
- /* Find longest common part */
+ // Find longest common part
if (mode == WILD_LONGEST && xp->xp_numfiles > 0) {
size_t len;
- for (len = 0; xp->xp_files[0][len]; ++len) {
- for (i = 0; i < xp->xp_numfiles; ++i) {
+ size_t mb_len = 1;
+ int c0;
+ int ci;
+
+ for (len = 0; xp->xp_files[0][len]; len += mb_len) {
+ if (has_mbyte) {
+ mb_len = (* mb_ptr2len)(&xp->xp_files[0][len]);
+ c0 = (* mb_ptr2char)(&xp->xp_files[0][len]);
+ } else {
+ c0 = xp->xp_files[0][len];
+ }
+ for (i = 1; i < xp->xp_numfiles; ++i) {
+ if (has_mbyte) {
+ ci =(* mb_ptr2char)(&xp->xp_files[i][len]);
+ } else {
+ ci = xp->xp_files[i][len];
+ }
+
if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
|| xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_SHELLCMD
|| xp->xp_context == EXPAND_BUFFERS)) {
- if (TOLOWER_LOC(xp->xp_files[i][len]) !=
- TOLOWER_LOC(xp->xp_files[0][len]))
+ if (vim_tolower(c0) != vim_tolower(ci)) {
break;
- } else if (xp->xp_files[i][len] != xp->xp_files[0][len])
+ }
+ } else if (c0 != ci) {
break;
+ }
}
if (i < xp->xp_numfiles) {
if (!(options & WILD_NO_BEEP)) {
@@ -2979,8 +2996,9 @@ ExpandOne (
break;
}
}
+
ss = (char_u *)xstrndup((char *)xp->xp_files[0], len);
- findex = -1; /* next p_wc gets first one */
+ findex = -1; // next p_wc gets first one
}
// Concatenate all matching names
@@ -3966,6 +3984,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file,
char_u *s, *e;
int flags = flagsarg;
int ret;
+ bool did_curdir = false;
/* for ":set path=" and ":set tags=" halve backslashes for escaped
* space */
@@ -3974,7 +3993,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file,
if (pat[i] == '\\' && pat[i + 1] == ' ')
STRMOVE(pat + i, pat + i + 1);
- flags |= EW_FILE | EW_EXEC;
+ flags |= EW_FILE | EW_EXEC | EW_SHELLCMD;
bool mustfree = false; // Track memory allocation for *path.
/* For an absolute name we don't use $PATH. */
@@ -3994,12 +4013,24 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file,
/*
* Go over all directories in $PATH. Expand matches in that directory and
- * collect them in "ga".
+ * collect them in "ga". When "." is not in $PATH also expaned for the
+ * current directory, to find "subdir/cmd".
*/
ga_init(&ga, (int)sizeof(char *), 10);
- for (s = path; *s != NUL; s = e) {
- if (*s == ' ')
- ++s; /* Skip space used for absolute path name. */
+ for (s = path; ; s = e) {
+ if (*s == NUL) {
+ if (did_curdir) {
+ break;
+ }
+ // Find directories in the current directory, path is empty.
+ did_curdir = true;
+ } else if (*s == '.') {
+ did_curdir = true;
+ }
+
+ if (*s == ' ') {
+ s++; // Skip space used for absolute path name.
+ }
e = vim_strchr(s, ':');
if (e == NULL)
@@ -4257,20 +4288,33 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options)
* Command line history stuff *
*********************************/
-/*
- * Translate a history character to the associated type number.
- */
-static int hist_char2type(int c)
+/// Translate a history character to the associated type number
+static HistoryType hist_char2type(const int c)
+ FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
{
- if (c == ':')
- return HIST_CMD;
- if (c == '=')
- return HIST_EXPR;
- if (c == '@')
- return HIST_INPUT;
- if (c == '>')
- return HIST_DEBUG;
- return HIST_SEARCH; /* must be '?' or '/' */
+ switch (c) {
+ case ':': {
+ return HIST_CMD;
+ }
+ case '=': {
+ return HIST_EXPR;
+ }
+ case '@': {
+ return HIST_INPUT;
+ }
+ case '>': {
+ return HIST_DEBUG;
+ }
+ case '/':
+ case '?': {
+ return HIST_SEARCH;
+ }
+ default: {
+ return HIST_INVALID;
+ }
+ }
+ // Silence -Wreturn-type
+ return 0;
}
/*
@@ -4439,28 +4483,38 @@ in_history (
return false;
}
-/*
- * Convert history name (from table above) to its HIST_ equivalent.
- * When "name" is empty, return "cmd" history.
- * Returns -1 for unknown history name.
- */
-int get_histtype(char_u *name)
+/// Convert history name to its HIST_ equivalent
+///
+/// Names are taken from the table above. When `name` is empty returns currently
+/// active history or HIST_DEFAULT, depending on `return_default` argument.
+///
+/// @param[in] name Converted name.
+/// @param[in] len Name length.
+/// @param[in] return_default Determines whether HIST_DEFAULT should be
+/// returned or value based on `ccline.cmdfirstc`.
+///
+/// @return Any value from HistoryType enum, including HIST_INVALID. May not
+/// return HIST_DEFAULT unless return_default is true.
+HistoryType get_histtype(const char_u *const name, const size_t len,
+ const bool return_default)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- int i;
- int len = (int)STRLEN(name);
-
- /* No argument: use current history. */
- if (len == 0)
- return hist_char2type(ccline.cmdfirstc);
+ // No argument: use current history.
+ if (len == 0) {
+ return return_default ? HIST_DEFAULT : hist_char2type(ccline.cmdfirstc);
+ }
- for (i = 0; history_names[i] != NULL; ++i)
- if (STRNICMP(name, history_names[i], len) == 0)
+ for (HistoryType i = 0; history_names[i] != NULL; i++) {
+ if (STRNICMP(name, history_names[i], len) == 0) {
return i;
+ }
+ }
- if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && name[1] == NUL)
+ if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && len == 1) {
return hist_char2type(name[0]);
+ }
- return -1;
+ return HIST_INVALID;
}
static int last_maptick = -1; /* last seen maptick */
@@ -4481,8 +4535,10 @@ add_to_history (
histentry_T *hisptr;
int len;
- if (hislen == 0) /* no history */
+ if (hislen == 0 || histype == HIST_INVALID) { // no history
return;
+ }
+ assert(histype != HIST_DEFAULT);
if (cmdmod.keeppatterns && histype == HIST_SEARCH)
return;
@@ -4832,23 +4888,20 @@ void ex_history(exarg_T *eap)
while (ASCII_ISALPHA(*end)
|| vim_strchr((char_u *)":=@>/?", *end) != NULL)
end++;
- i = *end;
- *end = NUL;
- histype1 = get_histtype(arg);
- if (histype1 == -1) {
- if (STRNICMP(arg, "all", STRLEN(arg)) == 0) {
+ histype1 = get_histtype(arg, end - arg, false);
+ if (histype1 == HIST_INVALID) {
+ if (STRNICMP(arg, "all", end - arg) == 0) {
histype1 = 0;
histype2 = HIST_COUNT-1;
} else {
- *end = i;
EMSG(_(e_trailing));
return;
}
} else
histype2 = histype1;
- *end = i;
- } else
+ } else {
end = arg;
+ }
if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL) {
EMSG(_(e_trailing));
return;
@@ -4956,7 +5009,6 @@ static int ex_window(void)
win_T *wp;
int i;
linenr_T lnum;
- int histtype;
garray_T winsizes;
char_u typestr[2];
int save_restart_edit = restart_edit;
@@ -5005,7 +5057,7 @@ static int ex_window(void)
/* Showing the prompt may have set need_wait_return, reset it. */
need_wait_return = FALSE;
- histtype = hist_char2type(cmdwin_type);
+ const int histtype = hist_char2type(cmdwin_type);
if (histtype == HIST_CMD || histtype == HIST_DEBUG) {
if (p_wc == TAB) {
add_map((char_u *)"<buffer> <Tab> <C-X><C-V>", INSERT);
@@ -5020,7 +5072,7 @@ static int ex_window(void)
/* Fill the buffer with the history. */
init_history();
- if (hislen > 0) {
+ if (hislen > 0 && histtype != HIST_INVALID) {
i = hisidx[histtype];
if (i >= 0) {
lnum = 0;
@@ -5136,6 +5188,8 @@ static int ex_window(void)
/* Don't execute autocommands while deleting the window. */
block_autocmds();
+ // Avoid command-line window first character being concealed
+ curwin->w_p_cole = 0;
wp = curwin;
bp = curbuf;
win_goto(old_curwin);
diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h
index 21da8b9d42..24eebdc303 100644
--- a/src/nvim/ex_getln.h
+++ b/src/nvim/ex_getln.h
@@ -27,11 +27,13 @@
/// Present history tables
typedef enum {
- HIST_CMD, ///< Colon commands.
- HIST_SEARCH, ///< Search commands.
- HIST_EXPR, ///< Expressions (e.g. from entering = register).
- HIST_INPUT, ///< input() lines.
- HIST_DEBUG, ///< Debug commands.
+ HIST_DEFAULT = -2, ///< Default (current) history.
+ HIST_INVALID = -1, ///< Unknown history.
+ HIST_CMD = 0, ///< Colon commands.
+ HIST_SEARCH, ///< Search commands.
+ HIST_EXPR, ///< Expressions (e.g. from entering = register).
+ HIST_INPUT, ///< input() lines.
+ HIST_DEBUG, ///< Debug commands.
} HistoryType;
/// Number of history tables
diff --git a/src/nvim/farsi.c b/src/nvim/farsi.c
index 47a132c0d0..61e17128ea 100644
--- a/src/nvim/farsi.c
+++ b/src/nvim/farsi.c
@@ -100,8 +100,9 @@ static char_u toF_Xor_X_(int c)
case F_HE :
tempc = _HE;
- if (p_ri &&
- (curwin->w_cursor.col + 1 < (colnr_T)STRLEN(get_cursor_line_ptr()))) {
+ if (p_ri
+ && (curwin->w_cursor.col + 1
+ < (colnr_T)STRLEN(get_cursor_line_ptr()))) {
inc_cursor();
if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) {
tempc = _HE_;
@@ -526,8 +527,8 @@ static void chg_l_toXor_X(void)
{
char_u tempc;
- if ((curwin->w_cursor.col != 0) &&
- (curwin->w_cursor.col + 1 == (colnr_T)STRLEN(get_cursor_line_ptr()))) {
+ if ((curwin->w_cursor.col != 0)
+ && (curwin->w_cursor.col + 1 == (colnr_T)STRLEN(get_cursor_line_ptr()))) {
return;
}
@@ -680,17 +681,17 @@ int fkmap(int c)
}
}
- if ((c < 0x100) &&
- (isalpha(c) ||
- (c == '&') ||
- (c == '^') ||
- (c == ';') ||
- (c == '\'') ||
- (c == ',') ||
- (c == '[') ||
- (c == ']') ||
- (c == '{') ||
- (c == '}'))) {
+ if ((c < 0x100)
+ && (isalpha(c)
+ || (c == '&')
+ || (c == '^')
+ || (c == ';')
+ || (c == '\'')
+ || (c == ',')
+ || (c == '[')
+ || (c == ']')
+ || (c == '{')
+ || (c == '}'))) {
chg_r_to_Xor_X_();
}
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index b213a42c52..beefc4238e 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -484,28 +484,21 @@ vim_findfile_init (
len = (int)(p - search_ctx->ffsc_fix_path) - 1;
STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len);
add_pathsep((char *)ff_expand_buffer);
- } else
+ } else {
len = (int)STRLEN(search_ctx->ffsc_fix_path);
+ }
if (search_ctx->ffsc_wc_path != NULL) {
wc_path = vim_strsave(search_ctx->ffsc_wc_path);
temp = xmalloc(STRLEN(search_ctx->ffsc_wc_path)
+ STRLEN(search_ctx->ffsc_fix_path + len)
+ 1);
- }
-
- if (temp == NULL || wc_path == NULL) {
- xfree(buf);
- xfree(temp);
+ STRCPY(temp, search_ctx->ffsc_fix_path + len);
+ STRCAT(temp, search_ctx->ffsc_wc_path);
+ xfree(search_ctx->ffsc_wc_path);
xfree(wc_path);
- goto error_return;
+ search_ctx->ffsc_wc_path = temp;
}
-
- STRCPY(temp, search_ctx->ffsc_fix_path + len);
- STRCAT(temp, search_ctx->ffsc_wc_path);
- xfree(search_ctx->ffsc_wc_path);
- xfree(wc_path);
- search_ctx->ffsc_wc_path = temp;
}
xfree(buf);
}
@@ -1046,41 +1039,44 @@ static ff_visited_list_hdr_T *ff_get_visited_list(char_u *filename, ff_visited_l
return retptr;
}
-/*
- * check if two wildcard paths are equal. Returns TRUE or FALSE.
- * They are equal if:
- * - both paths are NULL
- * - they have the same length
- * - char by char comparison is OK
- * - the only differences are in the counters behind a '**', so
- * '**\20' is equal to '**\24'
- */
-static int ff_wc_equal(char_u *s1, char_u *s2)
+// Check if two wildcard paths are equal.
+// They are equal if:
+// - both paths are NULL
+// - they have the same length
+// - char by char comparison is OK
+// - the only differences are in the counters behind a '**', so
+// '**\20' is equal to '**\24'
+static bool ff_wc_equal(char_u *s1, char_u *s2)
{
- int i;
+ int i, j;
+ int c1 = NUL;
+ int c2 = NUL;
int prev1 = NUL;
int prev2 = NUL;
- if (s1 == s2)
- return TRUE;
-
- if (s1 == NULL || s2 == NULL)
- return FALSE;
+ if (s1 == s2) {
+ return true;
+ }
- if (STRLEN(s1) != STRLEN(s2))
- return FAIL;
+ if (s1 == NULL || s2 == NULL) {
+ return false;
+ }
- for (i = 0; s1[i] != NUL && s2[i] != NUL; i += MB_PTR2LEN(s1 + i)) {
- int c1 = PTR2CHAR(s1 + i);
- int c2 = PTR2CHAR(s2 + i);
+ for (i = 0, j = 0; s1[i] != NUL && s2[j] != NUL;) {
+ c1 = PTR2CHAR(s1 + i);
+ c2 = PTR2CHAR(s2 + j);
if ((p_fic ? vim_tolower(c1) != vim_tolower(c2) : c1 != c2)
- && (prev1 != '*' || prev2 != '*'))
- return FAIL;
+ && (prev1 != '*' || prev2 != '*')) {
+ return false;
+ }
prev2 = prev1;
prev1 = c1;
+
+ i += MB_PTR2LEN(s1 + i);
+ j += MB_PTR2LEN(s2 + j);
}
- return TRUE;
+ return s1[i] == s2[j];
}
/*
@@ -1111,10 +1107,11 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *
if ((url && fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0)
|| (!url && vp->file_id_valid
&& os_fileid_equal(&(vp->file_id), &file_id))) {
- /* are the wildcard parts equal */
- if (ff_wc_equal(vp->ffv_wc_path, wc_path) == TRUE)
- /* already visited */
+ // are the wildcard parts equal
+ if (ff_wc_equal(vp->ffv_wc_path, wc_path)) {
+ // already visited
return FAIL;
+ }
}
}
@@ -1403,8 +1400,14 @@ find_file_in_path_option (
&& (ff_file_to_find[2] == NUL
|| vim_ispathsep(ff_file_to_find[2])))));
if (vim_isAbsName(ff_file_to_find)
- /* "..", "../path", "." and "./path": don't use the path_option */
+ // "..", "../path", "." and "./path": don't use the path_option
|| rel_to_curdir
+#if defined(WIN32)
+ // handle "\tmp" as absolute path
+ || vim_ispathsep(ff_file_to_find[0])
+ // handle "c:name" as absolute path
+ || (ff_file_to_find[0] != NUL && ff_file_to_find[1] == ':')
+#endif
) {
/*
* Absolute path, no need to use "path_option".
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 90987d0b3d..4d9e10fb85 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -45,7 +45,6 @@
#include "nvim/search.h"
#include "nvim/sha256.h"
#include "nvim/strings.h"
-#include "nvim/tempfile.h"
#include "nvim/ui.h"
#include "nvim/types.h"
#include "nvim/undo.h"
@@ -606,13 +605,14 @@ readfile (
* Don't do this for a "nofile" or "nowrite" buffer type. */
if (!bt_dontwrite(curbuf)) {
check_need_swap(newfile);
- if (!read_stdin && (curbuf != old_curbuf
- || (using_b_ffname && (old_b_ffname != curbuf->b_ffname))
- || (using_b_fname &&
- (old_b_fname != curbuf->b_fname)))) {
+ if (!read_stdin
+ && (curbuf != old_curbuf
+ || (using_b_ffname && (old_b_ffname != curbuf->b_ffname))
+ || (using_b_fname && (old_b_fname != curbuf->b_fname)))) {
EMSG(_(e_auchangedbuf));
- if (!read_buffer)
+ if (!read_buffer) {
close(fd);
+ }
return FAIL;
}
#ifdef UNIX
@@ -1749,8 +1749,9 @@ failed:
#ifdef HAVE_FD_CLOEXEC
else {
int fdflags = fcntl(fd, F_GETFD);
- if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
- fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC);
+ if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) {
+ (void)fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC);
+ }
}
#endif
xfree(buffer);
@@ -2139,9 +2140,10 @@ readfile_charconvert (
else {
close(*fdp); /* close the input file, ignore errors */
*fdp = -1;
- if (eval_charconvert(fenc, enc_utf8 ? (char_u *)"utf-8" : p_enc,
- fname, tmpname) == FAIL)
+ if (eval_charconvert((char *) fenc, enc_utf8 ? "utf-8" : (char *) p_enc,
+ (char *) fname, (char *) tmpname) == FAIL) {
errmsg = (char_u *)_("Conversion with 'charconvert' failed");
+ }
if (errmsg == NULL && (*fdp = os_open((char *)tmpname, O_RDONLY, 0)) < 0) {
errmsg = (char_u *)_("can't read output of 'charconvert'");
}
@@ -2576,7 +2578,7 @@ buf_write (
errmsg = (char_u *)_("is a directory");
goto fail;
}
- if (mch_nodetype(fname) != NODE_WRITABLE) {
+ if (os_nodetype((char *)fname) != NODE_WRITABLE) {
errnum = (char_u *)"E503: ";
errmsg = (char_u *)_("is not a file or writable device");
goto fail;
@@ -2588,11 +2590,11 @@ buf_write (
perm = -1;
}
}
-#else /* !UNIX */
+#else /* win32 */
/*
* Check for a writable device name.
*/
- c = mch_nodetype(fname);
+ c = os_nodetype((char *)fname);
if (c == NODE_OTHER) {
errnum = (char_u *)"E503: ";
errmsg = (char_u *)_("is not a file or writable device");
@@ -2688,7 +2690,6 @@ buf_write (
} else if ((bkc & BKC_AUTO)) { /* "auto" */
int i;
-# ifdef UNIX
/*
* Don't rename the file when:
* - it's a hard link
@@ -2699,9 +2700,7 @@ buf_write (
|| !os_fileinfo_link((char *)fname, &file_info)
|| !os_fileinfo_id_equal(&file_info, &file_info_old)) {
backup_copy = TRUE;
- } else
-# endif
- {
+ } else {
/*
* Check if we can create a file and set the owner/group to
* the ones from the original file.
@@ -3435,9 +3434,9 @@ restore_backup:
* with 'charconvert' to (overwrite) the output file.
*/
if (end != 0) {
- if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc,
- wfname, fname) == FAIL) {
- write_info.bw_conv_error = TRUE;
+ if (eval_charconvert(enc_utf8 ? "utf-8" : (char *) p_enc, (char *) fenc,
+ (char *) wfname, (char *) fname) == FAIL) {
+ write_info.bw_conv_error = true;
end = 0;
}
}
@@ -4372,8 +4371,8 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)
// (we need the full path in case :cd is used).
if (fname == NULL || *fname == NUL) {
retval = xmalloc(MAXPATHL + extlen + 3); // +3 for PATHSEP, "_" (Win), NUL
- if (os_dirname((char_u *)retval, MAXPATHL) == FAIL ||
- (fnamelen = strlen(retval)) == 0) {
+ if (os_dirname((char_u *)retval, MAXPATHL) == FAIL
+ || (fnamelen = strlen(retval)) == 0) {
xfree(retval);
return NULL;
}
@@ -4740,7 +4739,6 @@ buf_check_timestamp (
{
int retval = 0;
char_u *path;
- char_u *tbuf;
char *mesg = NULL;
char *mesg2 = "";
int helpmesg = FALSE;
@@ -4810,19 +4808,17 @@ buf_check_timestamp (
else
reason = "time";
- /*
- * Only give the warning if there are no FileChangedShell
- * autocommands.
- * Avoid being called recursively by setting "busy".
- */
- busy = TRUE;
- set_vim_var_string(VV_FCS_REASON, (char_u *)reason, -1);
- set_vim_var_string(VV_FCS_CHOICE, (char_u *)"", -1);
- ++allbuf_lock;
+ // Only give the warning if there are no FileChangedShell
+ // autocommands.
+ // Avoid being called recursively by setting "busy".
+ busy = true;
+ set_vim_var_string(VV_FCS_REASON, reason, -1);
+ set_vim_var_string(VV_FCS_CHOICE, "", -1);
+ allbuf_lock++;
n = apply_autocmds(EVENT_FILECHANGEDSHELL,
- buf->b_fname, buf->b_fname, FALSE, buf);
- --allbuf_lock;
- busy = FALSE;
+ buf->b_fname, buf->b_fname, false, buf);
+ allbuf_lock--;
+ busy = false;
if (n) {
if (!buf_valid(buf))
EMSG(_("E246: FileChangedShell autocommand deleted buffer"));
@@ -4876,35 +4872,39 @@ buf_check_timestamp (
if (mesg != NULL) {
path = home_replace_save(buf, buf->b_fname);
- if (!helpmesg)
+ if (!helpmesg) {
mesg2 = "";
- tbuf = xmalloc(STRLEN(path) + STRLEN(mesg) + STRLEN(mesg2) + 2);
- sprintf((char *)tbuf, mesg, path);
- /* Set warningmsg here, before the unimportant and output-specific
- * mesg2 has been appended. */
+ }
+ const size_t tbuf_len = STRLEN(path) + STRLEN(mesg) + STRLEN(mesg2) + 2;
+ char *const tbuf = xmalloc(tbuf_len);
+ snprintf(tbuf, tbuf_len, mesg, path);
+ // Set warningmsg here, before the unimportant and output-specific
+ // mesg2 has been appended.
set_vim_var_string(VV_WARNINGMSG, tbuf, -1);
if (can_reload) {
if (*mesg2 != NUL) {
- STRCAT(tbuf, "\n");
- STRCAT(tbuf, mesg2);
+ strncat(tbuf, "\n", tbuf_len);
+ strncat(tbuf, mesg2, tbuf_len);
+ }
+ if (do_dialog(VIM_WARNING, (char_u *) _("Warning"), (char_u *) tbuf,
+ (char_u *) _("&OK\n&Load File"), 1, NULL, true) == 2) {
+ reload = true;
}
- if (do_dialog(VIM_WARNING, (char_u *)_("Warning"), tbuf,
- (char_u *)_("&OK\n&Load File"), 1, NULL, TRUE) == 2)
- reload = TRUE;
} else if (State > NORMAL_BUSY || (State & CMDLINE) || already_warned) {
if (*mesg2 != NUL) {
- STRCAT(tbuf, "; ");
- STRCAT(tbuf, mesg2);
+ strncat(tbuf, "; ", tbuf_len);
+ strncat(tbuf, mesg2, tbuf_len);
}
EMSG(tbuf);
retval = 2;
} else {
if (!autocmd_busy) {
msg_start();
- msg_puts_attr(tbuf, hl_attr(HLF_E) + MSG_HIST);
- if (*mesg2 != NUL)
+ msg_puts_attr((char_u *) tbuf, hl_attr(HLF_E) + MSG_HIST);
+ if (*mesg2 != NUL) {
msg_puts_attr((char_u *)mesg2,
hl_attr(HLF_W) + MSG_HIST);
+ }
msg_clr_eos();
(void)msg_end();
if (emsg_silent == 0) {
@@ -5098,22 +5098,167 @@ void write_lnum_adjust(linenr_T offset)
}
#if defined(BACKSLASH_IN_FILENAME)
-/*
- * Convert all backslashes in fname to forward slashes in-place.
- */
+/// Convert all backslashes in fname to forward slashes in-place,
+/// unless when it looks like a URL.
void forward_slash(char_u *fname)
{
char_u *p;
- for (p = fname; *p != NUL; ++p)
- /* The Big5 encoding can have '\' in the trail byte. */
- if (enc_dbcs != 0 && (*mb_ptr2len)(p) > 1)
- ++p;
- else if (*p == '\\')
+ if (path_with_url(fname)) {
+ return;
+ }
+ for (p = fname; *p != NUL; p++) {
+ // The Big5 encoding can have '\' in the trail byte.
+ if (enc_dbcs != 0 && (*mb_ptr2len)(p) > 1) {
+ p++;
+ } else if (*p == '\\') {
*p = '/';
+ }
+ }
}
#endif
+/// Name of Vim's own temp dir. Ends in a slash.
+static char_u *vim_tempdir = NULL;
+
+/// Create a directory for private use by this instance of Neovim.
+/// This is done once, and the same directory is used for all temp files.
+/// This method avoids security problems because of symlink attacks et al.
+/// It's also a bit faster, because we only need to check for an existing
+/// file when creating the directory and not for each temp file.
+static void vim_maketempdir(void)
+{
+ static const char *temp_dirs[] = TEMP_DIR_NAMES;
+ // Try the entries in `TEMP_DIR_NAMES` to create the temp directory.
+ char_u template[TEMP_FILE_PATH_MAXLEN];
+ char_u path[TEMP_FILE_PATH_MAXLEN];
+ for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) {
+ // Expand environment variables, leave room for "/nvimXXXXXX/999999999"
+ expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22);
+ if (!os_isdir(template)) { // directory doesn't exist
+ continue;
+ }
+
+ add_pathsep((char *)template);
+ // Concatenate with temporary directory name pattern
+ STRCAT(template, "nvimXXXXXX");
+
+ if (os_mkdtemp((const char *)template, (char *)path) != 0) {
+ continue;
+ }
+
+ if (vim_settempdir((char *)path)) {
+ // Successfully created and set temporary directory so stop trying.
+ break;
+ } else {
+ // Couldn't set `vim_tempdir` to `path` so remove created directory.
+ os_rmdir((char *)path);
+ }
+ }
+}
+
+/// Delete "name" and everything in it, recursively.
+/// @param name The path which should be deleted.
+/// @return 0 for success, -1 if some file was not deleted.
+int delete_recursive(char_u *name)
+{
+ int result = 0;
+
+ if (os_isrealdir(name)) {
+ snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); // NOLINT
+
+ char_u **files;
+ int file_count;
+ char_u *exp = vim_strsave(NameBuff);
+ if (gen_expand_wildcards(1, &exp, &file_count, &files,
+ EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS
+ | EW_DODOT | EW_EMPTYOK) == OK) {
+ for (int i = 0; i < file_count; i++) {
+ if (delete_recursive(files[i]) != 0) {
+ result = -1;
+ }
+ }
+ FreeWild(file_count, files);
+ } else {
+ result = -1;
+ }
+
+ xfree(exp);
+ os_rmdir((char *)name);
+ } else {
+ result = os_remove((char *)name) == 0 ? 0 : -1;
+ }
+
+ return result;
+}
+
+/// Delete the temp directory and all files it contains.
+void vim_deltempdir(void)
+{
+ if (vim_tempdir != NULL) {
+ // remove the trailing path separator
+ path_tail(vim_tempdir)[-1] = NUL;
+ delete_recursive(vim_tempdir);
+ xfree(vim_tempdir);
+ vim_tempdir = NULL;
+ }
+}
+
+/// Get the name of temp directory. This directory would be created on the first
+/// call to this function.
+char_u *vim_gettempdir(void)
+{
+ if (vim_tempdir == NULL) {
+ vim_maketempdir();
+ }
+
+ return vim_tempdir;
+}
+
+/// Set Neovim own temporary directory name to `tempdir`. This directory should
+/// be already created. Expand this name to a full path and put it in
+/// `vim_tempdir`. This avoids that using `:cd` would confuse us.
+///
+/// @param tempdir must be no longer than MAXPATHL.
+///
+/// @return false if we run out of memory.
+static bool vim_settempdir(char *tempdir)
+{
+ char *buf = verbose_try_malloc(MAXPATHL + 2);
+ if (!buf) {
+ return false;
+ }
+ vim_FullName(tempdir, buf, MAXPATHL, false);
+ add_pathsep(buf);
+ vim_tempdir = (char_u *)xstrdup(buf);
+ xfree(buf);
+ return true;
+}
+
+/// Return a unique name that can be used for a temp file.
+///
+/// @note The temp file is NOT created.
+///
+/// @return pointer to the temp file name or NULL if Neovim can't create
+/// temporary directory for its own temporary files.
+char_u *vim_tempname(void)
+{
+ // Temp filename counter.
+ static uint32_t temp_count;
+
+ char_u *tempdir = vim_gettempdir();
+ if (!tempdir) {
+ return NULL;
+ }
+
+ // There is no need to check if the file exists, because we own the directory
+ // and nobody else creates a file in it.
+ char_u template[TEMP_FILE_PATH_MAXLEN];
+ snprintf((char *)template, TEMP_FILE_PATH_MAXLEN,
+ "%s%" PRIu32, tempdir, temp_count++);
+ return vim_strsave(template);
+}
+
/*
* Code for automatic commands.
@@ -7160,10 +7305,11 @@ char_u * file_pat_to_reg_pat(
else
reg_pat[i++] = '^';
endp = pat_end - 1;
- if (*endp == '*') {
- while (endp - pat > 0 && *endp == '*')
+ if (endp >= pat && *endp == '*') {
+ while (endp - pat > 0 && *endp == '*') {
endp--;
- add_dollar = FALSE;
+ }
+ add_dollar = false;
}
for (p = pat; *p && nested >= 0 && p <= endp; p++) {
switch (*p) {
@@ -7218,12 +7364,12 @@ char_u * file_pat_to_reg_pat(
#ifdef BACKSLASH_IN_FILENAME
&& no_bslash
#endif
- )
+ ) {
reg_pat[i++] = '?';
- else if (*p == ',' || *p == '%' || *p == '#'
- || *p == ' ' || *p == '{' || *p == '}')
+ } else if (*p == ',' || *p == '%' || *p == '#'
+ || ascii_isspace(*p) || *p == '{' || *p == '}') {
reg_pat[i++] = *p;
- else if (*p == '\\' && p[1] == '\\' && p[2] == '{') {
+ } else if (*p == '\\' && p[1] == '\\' && p[2] == '{') {
reg_pat[i++] = '\\';
reg_pat[i++] = '{';
p += 2;
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 6c135ef47b..ac3cf959c8 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -762,6 +762,10 @@ void clearFolding(win_T *win)
*/
void foldUpdate(win_T *wp, linenr_T top, linenr_T bot)
{
+ if (compl_busy) {
+ return;
+ }
+
fold_T *fp;
if (wp->w_buffer->terminal) {
return;
@@ -1695,14 +1699,14 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume,
did_emsg = FALSE;
if (*wp->w_p_fdt != NUL) {
- char_u dashes[MAX_LEVEL + 2];
+ char dashes[MAX_LEVEL + 2];
win_T *save_curwin;
int level;
char_u *p;
- /* Set "v:foldstart" and "v:foldend". */
- set_vim_var_nr(VV_FOLDSTART, lnum);
- set_vim_var_nr(VV_FOLDEND, lnume);
+ // Set "v:foldstart" and "v:foldend".
+ set_vim_var_nr(VV_FOLDSTART, (varnumber_T) lnum);
+ set_vim_var_nr(VV_FOLDEND, (varnumber_T) lnume);
/* Set "v:folddashes" to a string of "level" dashes. */
/* Set "v:foldlevel" to "level". */
@@ -1712,7 +1716,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume,
memset(dashes, '-', (size_t)level);
dashes[level] = NUL;
set_vim_var_string(VV_FOLDDASHES, dashes, -1);
- set_vim_var_nr(VV_FOLDLEVEL, (long)level);
+ set_vim_var_nr(VV_FOLDLEVEL, (varnumber_T) level);
/* skip evaluating foldtext on errors */
if (!got_fdt_error) {
@@ -2106,10 +2110,11 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level,
*/
if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level
&& flp->lvl > 0) {
- foldFind(gap, startlnum - 1, &fp);
+ (void)foldFind(gap, startlnum - 1, &fp);
if (fp >= ((fold_T *)gap->ga_data) + gap->ga_len
- || fp->fd_top >= startlnum)
+ || fp->fd_top >= startlnum) {
fp = NULL;
+ }
}
/*
@@ -2163,13 +2168,15 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level,
}
}
if (lvl < level + i) {
- foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2);
- if (fp2 != NULL)
+ (void)foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2);
+ if (fp2 != NULL) {
bot = fp2->fd_top + fp2->fd_len - 1 + fp->fd_top;
- } else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level)
- finish = TRUE;
- else
+ }
+ } else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level) {
+ finish = true;
+ } else {
break;
+ }
}
/* At the start of the first nested fold and at the end of the current
@@ -2672,7 +2679,7 @@ static void foldlevelExpr(fline_T *flp)
win = curwin;
curwin = flp->wp;
curbuf = flp->wp->w_buffer;
- set_vim_var_nr(VV_LNUM, lnum);
+ set_vim_var_nr(VV_LNUM, (varnumber_T) lnum);
flp->start = 0;
flp->had_end = flp->end;
diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h
index c31d21ec6d..af8558d40d 100644
--- a/src/nvim/func_attr.h
+++ b/src/nvim/func_attr.h
@@ -179,7 +179,8 @@
#endif
#ifdef DEFINE_FUNC_ATTRIBUTES
- #define FUNC_ATTR_ASYNC
+ #define FUNC_API_ASYNC
+ #define FUNC_API_NOEXPORT
#define FUNC_ATTR_MALLOC REAL_FATTR_MALLOC
#define FUNC_ATTR_ALLOC_SIZE(x) REAL_FATTR_ALLOC_SIZE(x)
#define FUNC_ATTR_ALLOC_SIZE_PROD(x,y) REAL_FATTR_ALLOC_SIZE_PROD(x,y)
diff --git a/src/nvim/garray.c b/src/nvim/garray.c
index e6cbd9332b..98cec69b54 100644
--- a/src/nvim/garray.c
+++ b/src/nvim/garray.c
@@ -188,12 +188,23 @@ void ga_concat(garray_T *gap, const char_u *restrict s)
return;
}
- int len = (int)strlen((char *) s);
+ ga_concat_len(gap, (const char *restrict) s, strlen((char *) s));
+}
+
+/// Concatenate a string to a growarray which contains characters
+///
+/// @param[out] gap Growarray to modify.
+/// @param[in] s String to concatenate.
+/// @param[in] len String length.
+void ga_concat_len(garray_T *const gap, const char *restrict s,
+ const size_t len)
+ FUNC_ATTR_NONNULL_ALL
+{
if (len) {
- ga_grow(gap, len);
+ ga_grow(gap, (int) len);
char *data = gap->ga_data;
- memcpy(data + gap->ga_len, s, (size_t)len);
- gap->ga_len += len;
+ memcpy(data + gap->ga_len, s, len);
+ gap->ga_len += (int) len;
}
}
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 89d22ad811..dbf0322d78 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -144,7 +144,7 @@ static int KeyNoremap = 0; /* remapping flags */
static char_u typebuf_init[TYPELEN_INIT]; /* initial typebuf.tb_buf */
static char_u noremapbuf_init[TYPELEN_INIT]; /* initial typebuf.tb_noremap */
-static int last_recorded_len = 0; /* number of last recorded chars */
+static size_t last_recorded_len = 0; // number of last recorded chars
static const uint8_t ui_toggle[] = { K_SPECIAL, KS_EXTRA, KE_PASTE, 0 };
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -211,7 +211,7 @@ char_u *get_recorded(void)
* (possibly mapped) characters that stopped the recording.
*/
len = STRLEN(p);
- if ((int)len >= last_recorded_len) {
+ if (len >= last_recorded_len) {
len -= last_recorded_len;
p[len] = NUL;
}
@@ -243,13 +243,15 @@ static void
add_buff (
buffheader_T *buf,
char_u *s,
- long slen /* length of "s" or -1 */
+ ssize_t slen // length of "s" or -1
)
{
- if (slen < 0)
- slen = (long)STRLEN(s);
- if (slen == 0) /* don't add empty strings */
+ if (slen < 0) {
+ slen = (ssize_t)STRLEN(s);
+ }
+ if (slen == 0) { // don't add empty strings
return;
+ }
if (buf->bh_first.b_next == NULL) { /* first add to list */
buf->bh_space = 0;
@@ -263,18 +265,19 @@ add_buff (
STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1);
buf->bh_index = 0;
- ssize_t len;
- if (buf->bh_space >= (int)slen) {
+ size_t len;
+ if (buf->bh_space >= (size_t)slen) {
len = STRLEN(buf->bh_curr->b_str);
STRLCPY(buf->bh_curr->b_str + len, s, slen + 1);
- buf->bh_space -= slen;
+ buf->bh_space -= (size_t)slen;
} else {
- if (slen < MINIMAL_SIZE)
+ if (slen < MINIMAL_SIZE) {
len = MINIMAL_SIZE;
- else
- len = slen;
+ } else {
+ len = (size_t)slen;
+ }
buffblock_T *p = xmalloc(sizeof(buffblock_T) + len);
- buf->bh_space = (int)(len - slen);
+ buf->bh_space = len - (size_t)slen;
STRLCPY(p->b_str, s, slen + 1);
p->b_next = buf->bh_curr->b_next;
@@ -317,11 +320,11 @@ static void add_char_buff(buffheader_T *buf, int c)
if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) {
/* translate special key code into three byte sequence */
temp[0] = K_SPECIAL;
- temp[1] = K_SECOND(c);
- temp[2] = K_THIRD(c);
+ temp[1] = (char_u)K_SECOND(c);
+ temp[2] = (char_u)K_THIRD(c);
temp[3] = NUL;
} else {
- temp[0] = c;
+ temp[0] = (char_u)c;
temp[1] = NUL;
}
add_buff(buf, temp, -1L);
@@ -694,10 +697,11 @@ static int read_redo(int init, int old_redo)
bp = bp->b_next;
p = bp->b_str;
}
- buf[i] = c;
- if (i == n - 1) { /* last byte of a character */
- if (n != 1)
+ buf[i] = (char_u)c;
+ if (i == n - 1) { // last byte of a character
+ if (n != 1) {
c = (*mb_ptr2char)(buf);
+ }
break;
}
c = *p;
@@ -882,8 +886,8 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent)
setcursor();
return FAIL;
}
- s1 = xmalloc(newlen);
- s2 = xmalloc(newlen);
+ s1 = xmalloc((size_t)newlen);
+ s2 = xmalloc((size_t)newlen);
typebuf.tb_buflen = newlen;
/* copy the old chars, before the insertion point */
@@ -937,7 +941,7 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent)
nrm = noremap;
for (i = 0; i < addlen; ++i)
typebuf.tb_noremap[typebuf.tb_off + i + offset] =
- (--nrm >= 0) ? val : RM_YES;
+ (char_u)((--nrm >= 0) ? val : RM_YES);
/* tb_maplen and tb_silent only remember the length of mapped and/or
* silent mappings at the start of the buffer, assuming that a mapped
@@ -965,8 +969,8 @@ void ins_char_typebuf(int c)
char_u buf[MB_MAXBYTES + 1];
if (IS_SPECIAL(c)) {
buf[0] = K_SPECIAL;
- buf[1] = K_SECOND(c);
- buf[2] = K_THIRD(c);
+ buf[1] = (char_u)K_SECOND(c);
+ buf[2] = (char_u)K_THIRD(c);
buf[3] = NUL;
} else {
buf[(*mb_char2bytes)(c, buf)] = NUL;
@@ -1083,25 +1087,25 @@ void del_typebuf(int len, int offset)
* Write typed characters to script file.
* If recording is on put the character in the recordbuffer.
*/
-static void gotchars(char_u *chars, int len)
+static void gotchars(char_u *chars, size_t len)
{
char_u *s = chars;
int c;
char_u buf[2];
- int todo = len;
- /* remember how many chars were last recorded */
- if (Recording)
+ // remember how many chars were last recorded
+ if (Recording) {
last_recorded_len += len;
+ }
buf[1] = NUL;
- while (todo--) {
- /* Handle one byte at a time; no translation to be done. */
+ while (len--) {
+ // Handle one byte at a time; no translation to be done.
c = *s++;
updatescript(c);
if (Recording) {
- buf[0] = c;
+ buf[0] = (char_u)c;
add_buff(&recordbuff, buf, 1L);
}
}
@@ -1380,13 +1384,15 @@ int vgetc(void)
} else {
mod_mask = 0x0;
last_recorded_len = 0;
- for (;; ) { /* this is done twice if there are modifiers */
- if (mod_mask) { /* no mapping after modifier has been read */
+ for (;; ) { // this is done twice if there are modifiers
+ bool did_inc = false;
+ if (mod_mask) { // no mapping after modifier has been read
++no_mapping;
++allow_keys;
+ did_inc = true; // mod_mask may change value
}
- c = vgetorpeek(TRUE);
- if (mod_mask) {
+ c = vgetorpeek(true);
+ if (did_inc) {
--no_mapping;
--allow_keys;
}
@@ -1463,10 +1469,10 @@ int vgetc(void)
* Note: This will loop until enough bytes are received!
*/
if (has_mbyte && (n = MB_BYTE2LEN_CHECK(c)) > 1) {
- ++no_mapping;
- buf[0] = c;
- for (i = 1; i < n; ++i) {
- buf[i] = vgetorpeek(TRUE);
+ no_mapping++;
+ buf[0] = (char_u)c;
+ for (i = 1; i < n; i++) {
+ buf[i] = (char_u)vgetorpeek(true);
if (buf[i] == K_SPECIAL
) {
/* Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence,
@@ -1560,7 +1566,7 @@ int char_avail(void)
{
int retval;
- ++no_mapping;
+ no_mapping++;
retval = vpeekc();
--no_mapping;
return retval != NUL;
@@ -1709,7 +1715,7 @@ static int vgetorpeek(int advance)
if (advance) {
/* Also record this character, it might be needed to
* get out of Insert mode. */
- *typebuf.tb_buf = c;
+ *typebuf.tb_buf = (char_u)c;
gotchars(typebuf.tb_buf, 1);
}
cmd_silent = FALSE;
@@ -1875,19 +1881,19 @@ static int vgetorpeek(int advance)
match = typebuf_match_len(p_pt, &mlen);
}
if (match) {
- /* write chars to script file(s) */
- if (mlen > typebuf.tb_maplen)
- gotchars(typebuf.tb_buf + typebuf.tb_off
- + typebuf.tb_maplen,
- mlen - typebuf.tb_maplen);
+ // write chars to script file(s)
+ if (mlen > typebuf.tb_maplen) {
+ gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen,
+ (size_t)(mlen - typebuf.tb_maplen));
+ }
del_typebuf(mlen, 0); /* remove the chars */
set_option_value((char_u *)"paste",
(long)!p_paste, NULL, 0);
if (!(State & INSERT)) {
msg_col = 0;
- msg_row = Rows - 1;
- msg_clr_eos(); /* clear ruler */
+ msg_row = (int)Rows - 1;
+ msg_clr_eos(); // clear ruler
}
status_redraw_all();
redraw_statuslines();
@@ -1973,11 +1979,11 @@ static int vgetorpeek(int advance)
char_u *save_m_keys;
char_u *save_m_str;
- /* write chars to script file(s) */
- if (keylen > typebuf.tb_maplen)
- gotchars(typebuf.tb_buf + typebuf.tb_off
- + typebuf.tb_maplen,
- keylen - typebuf.tb_maplen);
+ // write chars to script file(s)
+ if (keylen > typebuf.tb_maplen) {
+ gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen,
+ (size_t)(keylen - typebuf.tb_maplen));
+ }
cmd_silent = (typebuf.tb_silent > 0);
del_typebuf(keylen, 0); /* remove the mapped keys */
@@ -2415,7 +2421,7 @@ inchar (
else
return -1;
} else {
- buf[0] = script_char;
+ buf[0] = (char_u)script_char;
len = 1;
}
}
@@ -2451,7 +2457,7 @@ inchar (
* Fill up to a third of the buffer, because each character may be
* tripled below.
*/
- len = os_inchar(buf, maxlen / 3, wait_time, tb_change_cnt);
+ len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt);
}
if (typebuf_changed(tb_change_cnt))
@@ -2494,8 +2500,8 @@ fix_input_buffer (
&& !script
&& (i < 2 || p[1] != KS_EXTRA))) {
memmove(p + 3, p + 1, (size_t)i);
- p[2] = K_THIRD(p[0]);
- p[1] = K_SECOND(p[0]);
+ p[2] = (char_u)K_THIRD(p[0]);
+ p[1] = (char_u)K_SECOND(p[0]);
p[0] = K_SPECIAL;
p += 2;
len += 2;
@@ -2571,11 +2577,11 @@ do_map (
int new_hash;
mapblock_T **abbr_table;
mapblock_T **map_table;
- int unique = FALSE;
- int nowait = FALSE;
- int silent = FALSE;
- int special = FALSE;
- int expr = FALSE;
+ bool unique = false;
+ bool nowait = false;
+ bool silent = false;
+ bool special = false;
+ bool expr = false;
int noremap;
char_u *orig_rhs;
@@ -2607,7 +2613,7 @@ do_map (
*/
if (STRNCMP(keys, "<nowait>", 8) == 0) {
keys = skipwhite(keys + 8);
- nowait = TRUE;
+ nowait = true;
continue;
}
@@ -2616,7 +2622,7 @@ do_map (
*/
if (STRNCMP(keys, "<silent>", 8) == 0) {
keys = skipwhite(keys + 8);
- silent = TRUE;
+ silent = true;
continue;
}
@@ -2625,7 +2631,7 @@ do_map (
*/
if (STRNCMP(keys, "<special>", 9) == 0) {
keys = skipwhite(keys + 9);
- special = TRUE;
+ special = true;
continue;
}
@@ -2643,7 +2649,7 @@ do_map (
*/
if (STRNCMP(keys, "<expr>", 6) == 0) {
keys = skipwhite(keys + 6);
- expr = TRUE;
+ expr = true;
continue;
}
/*
@@ -2651,7 +2657,7 @@ do_map (
*/
if (STRNCMP(keys, "<unique>", 8) == 0) {
keys = skipwhite(keys + 8);
- unique = TRUE;
+ unique = true;
continue;
}
break;
@@ -2667,13 +2673,14 @@ do_map (
p = keys;
do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
while (*p && (maptype == 1 || !ascii_iswhite(*p))) {
- if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) &&
- p[1] != NUL)
- ++p; /* skip CTRL-V or backslash */
- ++p;
+ if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) && p[1] != NUL) {
+ p++; // skip CTRL-V or backslash
+ }
+ p++;
}
- if (*p != NUL)
+ if (*p != NUL) {
*p++ = NUL;
+ }
p = skipwhite(p);
rhs = p;
@@ -2686,22 +2693,24 @@ do_map (
goto theend;
}
- /*
- * If mapping has been given as ^V<C_UP> say, then replace the term codes
- * with the appropriate two bytes. If it is a shifted special key, unshift
- * it too, giving another two bytes.
- * replace_termcodes() may move the result to allocated memory, which
- * needs to be freed later (*keys_buf and *arg_buf).
- * replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
- */
- if (haskey)
- keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, special);
+ // If mapping has been given as ^V<C_UP> say, then replace the term codes
+ // with the appropriate two bytes. If it is a shifted special key, unshift
+ // it too, giving another two bytes.
+ // replace_termcodes() may move the result to allocated memory, which
+ // needs to be freed later (*keys_buf and *arg_buf).
+ // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
+ if (haskey) {
+ keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, special,
+ CPO_TO_CPO_FLAGS);
+ }
orig_rhs = rhs;
if (hasarg) {
- if (STRICMP(rhs, "<nop>") == 0) /* "<Nop>" means nothing */
+ if (STRICMP(rhs, "<nop>") == 0) { // "<Nop>" means nothing
rhs = (char_u *)"";
- else
- rhs = replace_termcodes(rhs, &arg_buf, FALSE, TRUE, special);
+ } else {
+ rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, special,
+ CPO_TO_CPO_FLAGS);
+ }
}
/*
@@ -2913,9 +2922,9 @@ do_map (
did_it = TRUE;
}
}
- if (mp->m_mode == 0) { /* entry can be deleted */
- map_free(mpp);
- continue; /* continue with *mpp */
+ if (mp->m_mode == 0) { // entry can be deleted
+ mapblock_free(mpp);
+ continue; // continue with *mpp
}
/*
@@ -3010,7 +3019,7 @@ theend:
* Delete one entry from the abbrlist or maphash[].
* "mpp" is a pointer to the m_next field of the PREVIOUS entry!
*/
-static void map_free(mapblock_T **mpp)
+static void mapblock_free(mapblock_T **mpp)
{
mapblock_T *mp;
@@ -3078,7 +3087,7 @@ int get_map_mode(char_u **cmdp, int forceit)
* Clear all mappings or abbreviations.
* 'abbr' should be FALSE for mappings, TRUE for abbreviations.
*/
-void map_clear(char_u *cmdp, char_u *arg, int forceit, int abbr)
+void map_clear_mode(char_u *cmdp, char_u *arg, int forceit, int abbr)
{
int mode;
int local;
@@ -3130,8 +3139,8 @@ map_clear_int (
mp = *mpp;
if (mp->m_mode & mode) {
mp->m_mode &= ~mode;
- if (mp->m_mode == 0) { /* entry can be deleted */
- map_free(mpp);
+ if (mp->m_mode == 0) { // entry can be deleted
+ mapblock_free(mpp);
continue;
}
/*
@@ -3268,7 +3277,8 @@ int map_to_exists(char_u *str, char_u *modechars, int abbr)
char_u *buf;
int retval;
- rhs = replace_termcodes(str, &buf, FALSE, TRUE, FALSE);
+ rhs = replace_termcodes(str, STRLEN(str), &buf, false, true, false,
+ CPO_TO_CPO_FLAGS);
if (vim_strchr(modechars, 'n') != NULL)
mode |= NORMAL;
@@ -3463,7 +3473,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
mp = maphash[hash];
for (; mp; mp = mp->m_next) {
if (mp->m_mode & expand_mapmodes) {
- p = translate_mapping(mp->m_keys, TRUE);
+ p = translate_mapping(mp->m_keys, true, CPO_TO_CPO_FLAGS);
if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) {
if (round == 1)
++count;
@@ -3481,7 +3491,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
break; /* for (round) */
if (round == 1) {
- *file = (char_u **)xmalloc(count * sizeof(char_u *));
+ *file = (char_u **)xmalloc((size_t)count * sizeof(char_u *));
}
} /* for (round) */
@@ -3645,8 +3655,8 @@ int check_abbr(int c, char_u *ptr, int col, int mincol)
/* special key code, split up */
if (IS_SPECIAL(c) || c == K_SPECIAL) {
tb[j++] = K_SPECIAL;
- tb[j++] = K_SECOND(c);
- tb[j++] = K_THIRD(c);
+ tb[j++] = (char_u)K_SECOND(c);
+ tb[j++] = (char_u)K_THIRD(c);
} else {
if (c < ABBR_OFF && (c < ' ' || c > '~'))
tb[j++] = Ctrl_V; /* special char needs CTRL-V */
@@ -3655,8 +3665,9 @@ int check_abbr(int c, char_u *ptr, int col, int mincol)
if (c >= ABBR_OFF)
c -= ABBR_OFF;
j += (*mb_char2bytes)(c, tb + j);
- } else
- tb[j++] = c;
+ } else {
+ tb[j++] = (char_u)c;
+ }
}
tb[j] = NUL;
/* insert the last typed char */
@@ -4188,14 +4199,15 @@ void add_map(char_u *map, int mode)
// Returns NULL when there is a problem.
static char_u * translate_mapping (
char_u *str,
- int expmap // TRUE when expanding mappings on command-line
+ int expmap, // True when expanding mappings on command-line
+ int cpo_flags // Value of various flags present in &cpo
)
{
garray_T ga;
ga_init(&ga, 1, 40);
- int cpo_bslash = (vim_strchr(p_cpo, CPO_BSLASH) != NULL);
- int cpo_special = (vim_strchr(p_cpo, CPO_SPECI) != NULL);
+ bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH);
+ bool cpo_special = !(cpo_flags&FLAG_CPO_SPECI);
for (; *str; ++str) {
int c = *str;
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 697a4a765a..dafb75ca87 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -4,17 +4,7 @@
#include <stdbool.h>
#include <inttypes.h>
-// EXTERN is only defined in main.c. That's where global variables are
-// actually defined and initialized.
-#ifndef EXTERN
-# define EXTERN extern
-# define INIT(...)
-#else
-# ifndef INIT
-# define INIT(...) __VA_ARGS__
-# endif
-#endif
-
+#include "nvim/macros.h"
#include "nvim/ex_eval.h"
#include "nvim/iconv.h"
#include "nvim/mbyte.h"
@@ -100,6 +90,12 @@
# define VIMRC_FILE ".nvimrc"
#endif
+typedef enum {
+ kNone = -1,
+ kFalse = 0,
+ kTrue = 1,
+} TriState;
+
/* Values for "starting" */
#define NO_SCREEN 2 /* no screen updating yet */
#define NO_BUFFERS 1 /* not all buffers loaded yet */
@@ -208,6 +204,10 @@ EXTERN int compl_length INIT(= 0);
* stop looking for matches. */
EXTERN int compl_interrupted INIT(= FALSE);
+// Set when doing something for completion that may call edit() recursively,
+// which is not allowed. Also used to disable folding during completion
+EXTERN int compl_busy INIT(= false);
+
/* List of flags for method of completion. */
EXTERN int compl_cont_status INIT(= 0);
# define CONT_ADDING 1 /* "normal" or "adding" expansion */
@@ -293,10 +293,11 @@ EXTERN int msg_no_more INIT(= FALSE); /* don't use more prompt, truncate
EXTERN char_u *sourcing_name INIT( = NULL); /* name of error message source */
EXTERN linenr_T sourcing_lnum INIT(= 0); /* line number of the source file */
-EXTERN int ex_nesting_level INIT(= 0); /* nesting level */
-EXTERN int debug_break_level INIT(= -1); /* break below this level */
-EXTERN int debug_did_msg INIT(= FALSE); /* did "debug mode" message */
-EXTERN int debug_tick INIT(= 0); /* breakpoint change count */
+EXTERN int ex_nesting_level INIT(= 0); // nesting level
+EXTERN int debug_break_level INIT(= -1); // break below this level
+EXTERN int debug_did_msg INIT(= false); // did "debug mode" message
+EXTERN int debug_tick INIT(= 0); // breakpoint change count
+EXTERN int debug_backtrace_level INIT(= 0); // breakpoint backtrace level
/* Values for "do_profiling". */
#define PROF_NONE 0 /* profiling not started */
@@ -503,6 +504,7 @@ EXTERN int cterm_normal_fg_bold INIT(= 0);
EXTERN int cterm_normal_bg_color INIT(= 0);
EXTERN RgbValue normal_fg INIT(= -1);
EXTERN RgbValue normal_bg INIT(= -1);
+EXTERN RgbValue normal_sp INIT(= -1);
EXTERN int autocmd_busy INIT(= FALSE); /* Is apply_autocmds() busy? */
EXTERN int autocmd_no_enter INIT(= FALSE); /* *Enter autocmds disabled */
@@ -912,8 +914,8 @@ EXTERN int KeyTyped; // TRUE if user typed current char
EXTERN int KeyStuffed; // TRUE if current char from stuffbuf
EXTERN int maptick INIT(= 0); // tick for each non-mapped char
-EXTERN char_u chartab[256]; /* table used in charset.c; See
- init_chartab() for explanation */
+EXTERN uint8_t chartab[256]; // table used in charset.c; See
+ // init_chartab() for explanation
EXTERN int must_redraw INIT(= 0); /* type of redraw necessary */
EXTERN int skip_redraw INIT(= FALSE); /* skip redraw once */
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index ab8959239b..916d27a964 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -32,7 +32,6 @@
#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/version.h"
-#include "nvim/tempfile.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@@ -2191,18 +2190,19 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen);
}
- /* Check if need to use Courier for ASCII code range, and if so pick up
- * the encoding to use */
- prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
- (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0])
- == 'y');
+ // Check if need to use Courier for ASCII code range, and if so pick up
+ // the encoding to use
+ prt_use_courier = (
+ mbfont_opts[OPT_MBFONT_USECOURIER].present
+ && (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y'));
if (prt_use_courier) {
- /* Use national ASCII variant unless ASCII wanted */
- if (mbfont_opts[OPT_MBFONT_ASCII].present &&
- (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
+ // Use national ASCII variant unless ASCII wanted
+ if (mbfont_opts[OPT_MBFONT_ASCII].present
+ && (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y')) {
prt_ascii_encoding = "ascii";
- else
+ } else {
prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
+ }
}
prt_ps_font = &prt_ps_mb_font;
@@ -2780,11 +2780,13 @@ void mch_print_end(prt_settings_T *psettings)
}
prt_message((char_u *)_("Sending to printer..."));
- /* Not printing to a file: use 'printexpr' to print the file. */
- if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
+ // Not printing to a file: use 'printexpr' to print the file.
+ if (eval_printexpr((char *) prt_ps_file_name, (char *) psettings->arguments)
+ == FAIL) {
EMSG(_("E365: Failed to print PostScript file"));
- else
+ } else {
prt_message((char_u *)_("Print job sent."));
+ }
}
mch_print_cleanup();
@@ -3028,10 +3030,10 @@ int mch_print_text_out(char_u *p, size_t len)
prt_text_run += char_width;
prt_pos_x += char_width;
- /* The downside of fp - use relative error on right margin check */
+ // The downside of fp - use relative error on right margin check
next_pos = prt_pos_x + prt_char_width;
- need_break = (next_pos > prt_right_margin) &&
- ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
+ need_break = ((next_pos > prt_right_margin)
+ && ((next_pos - prt_right_margin) > (prt_right_margin * 1e-5)));
if (need_break)
prt_flush_buffer();
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index d236501b3f..2f9ec0b3ff 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -27,7 +27,6 @@
#include "nvim/quickfix.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
-#include "nvim/tempfile.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@@ -1063,8 +1062,8 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose,
if (qf_init(wp, tmp, (char_u *)"%f%*\\t%l%*\\t%m",
*qfpos == '-', cmdline) > 0) {
if (postponed_split != 0) {
- win_split(postponed_split > 0 ? postponed_split : 0,
- postponed_split_flags);
+ (void)win_split(postponed_split > 0 ? postponed_split : 0,
+ postponed_split_flags);
RESET_BINDING(curwin);
postponed_split = 0;
}
@@ -1632,77 +1631,79 @@ static char *cs_pathcomponents(char *path)
return s;
}
-/*
- * PRIVATE: cs_print_tags_priv
- *
- * called from cs_manage_matches()
- */
+/// Print cscope output that was converted into ctags style entries.
+///
+/// Only called from cs_manage_matches().
+///
+/// @param matches Array of cscope lines in ctags style. Every entry was
+// produced with a format string of the form
+// "%s\t%s\t%s;\"\t%s" or
+// "%s\t%s\t%s;\""
+// by cs_make_vim_style_matches().
+/// @param cntxts Context for matches.
+/// @param num_matches Number of entries in matches/cntxts, always greater 0.
static void cs_print_tags_priv(char **matches, char **cntxts,
- size_t num_matches)
+ size_t num_matches) FUNC_ATTR_NONNULL_ALL
{
- char *ptag;
- char *fname, *lno, *extra, *tbuf;
- size_t num;
- char *globalcntx = "GLOBAL";
- char *context;
- char *cstag_msg = _("Cscope tag: %s");
+ char *globalcntx = "GLOBAL";
+ char *cstag_msg = _("Cscope tag: %s");
- assert (num_matches > 0);
+ assert(num_matches > 0);
+ assert(strcnt(matches[0], '\t') >= 2);
- tbuf = xmalloc(strlen(matches[0]) + 1);
+ char *ptag = matches[0];
+ char *ptag_end = strchr(ptag, '\t');
+ assert(ptag_end >= ptag);
+ // NUL terminate tag string in matches[0].
+ *ptag_end = NUL;
- strcpy(tbuf, matches[0]);
- ptag = strtok(tbuf, "\t");
-
- size_t newsize = strlen(cstag_msg) + strlen(ptag);
+ // The "%s" in cstag_msg won't appear in the result string, so we don't need
+ // extra memory for terminating NUL.
+ size_t newsize = strlen(cstag_msg) + (size_t)(ptag_end - ptag);
char *buf = xmalloc(newsize);
size_t bufsize = newsize; // Track available bufsize
- (void)sprintf(buf, cstag_msg, ptag);
+ (void)snprintf(buf, bufsize, cstag_msg, ptag);
MSG_PUTS_ATTR(buf, hl_attr(HLF_T));
+ msg_clr_eos();
- xfree(tbuf);
+ // restore matches[0]
+ *ptag_end = '\t';
- MSG_PUTS_ATTR(_("\n # line"), hl_attr(HLF_T)); /* strlen is 7 */
+ // Column headers for match number, line number and filename.
+ MSG_PUTS_ATTR(_("\n # line"), hl_attr(HLF_T));
msg_advance(msg_col + 2);
MSG_PUTS_ATTR(_("filename / context / line\n"), hl_attr(HLF_T));
- num = 1;
for (size_t i = 0; i < num_matches; i++) {
- size_t idx = i;
-
- /* if we really wanted to, we could avoid this malloc and strcpy
- * by parsing matches[i] on the fly and placing stuff into buf
- * directly, but that's too much of a hassle
- */
- tbuf = xmalloc(strlen(matches[idx]) + 1);
- (void)strcpy(tbuf, matches[idx]);
-
- if (strtok(tbuf, (const char *)"\t") == NULL)
- continue;
- if ((fname = strtok(NULL, (const char *)"\t")) == NULL)
- continue;
- if ((lno = strtok(NULL, (const char *)"\t")) == NULL)
- continue;
- extra = strtok(NULL, (const char *)"\t");
-
- lno[strlen(lno)-2] = '\0'; /* ignore ;" at the end */
+ assert(strcnt(matches[i], '\t') >= 2);
+
+ // Parse filename, line number and optional part.
+ char *fname = strchr(matches[i], '\t') + 1;
+ char *fname_end = strchr(fname, '\t');
+ // Replace second '\t' in matches[i] with NUL to terminate fname.
+ *fname_end = NUL;
+
+ char *lno = fname_end + 1;
+ char *extra = xstrchrnul(lno, '\t');
+ // Ignore ;" at the end of lno.
+ char *lno_end = extra - 2;
+ *lno_end = NUL;
+ // Do we have an optional part?
+ extra = *extra ? extra + 1 : NULL;
const char *csfmt_str = "%4zu %6s ";
- /* hopefully 'num' (num of matches) will be less than 10^16 */
- newsize = strlen(csfmt_str) + 16 + strlen(lno);
+ // hopefully num_matches will be less than 10^16
+ newsize = strlen(csfmt_str) + 16 + (size_t)(lno_end - lno);
if (bufsize < newsize) {
buf = xrealloc(buf, newsize);
bufsize = newsize;
}
- (void)sprintf(buf, csfmt_str, num, lno);
+ (void)snprintf(buf, bufsize, csfmt_str, i + 1, lno);
MSG_PUTS_ATTR(buf, hl_attr(HLF_CM));
MSG_PUTS_LONG_ATTR(cs_pathcomponents(fname), hl_attr(HLF_CM));
- /* compute the required space for the context */
- if (cntxts[idx] != NULL)
- context = cntxts[idx];
- else
- context = globalcntx;
+ // compute the required space for the context
+ char *context = cntxts[i] ? cntxts[i] : globalcntx;
const char *cntxformat = " <<%s>>";
// '%s' won't appear in result string, so:
@@ -1713,11 +1714,13 @@ static void cs_print_tags_priv(char **matches, char **cntxts,
buf = xrealloc(buf, newsize);
bufsize = newsize;
}
- (void)sprintf(buf, cntxformat, context);
+ int buf_len = snprintf(buf, bufsize, cntxformat, context);
+ assert(buf_len >= 0);
- /* print the context only if it fits on the same line */
- if (msg_col + (int)strlen(buf) >= (int)Columns)
+ // Print the context only if it fits on the same line.
+ if (msg_col + buf_len >= (int)Columns) {
msg_putchar('\n');
+ }
msg_advance(12);
MSG_PUTS_LONG(buf);
msg_putchar('\n');
@@ -1726,23 +1729,23 @@ static void cs_print_tags_priv(char **matches, char **cntxts,
MSG_PUTS_LONG(extra);
}
- xfree(tbuf); /* only after printing extra due to strtok use */
+ // restore matches[i]
+ *fname_end = '\t';
+ *lno_end = ';';
- if (msg_col)
+ if (msg_col) {
msg_putchar('\n');
+ }
os_breakcheck();
if (got_int) {
- got_int = FALSE; /* don't print any more matches */
+ got_int = false; // don't print any more matches
break;
}
-
- num++;
- } /* for all matches */
+ }
xfree(buf);
-} /* cs_print_tags_priv */
-
+}
/*
* PRIVATE: cs_read_prompt
@@ -2077,12 +2080,13 @@ static int cs_show(exarg_T *eap)
if (csinfo[i].fname == NULL)
continue;
- if (csinfo[i].ppath != NULL)
- (void)smsg("%2zu %-5" PRId64 " %-34s %-32s",
- i, (long)csinfo[i].pid, csinfo[i].fname, csinfo[i].ppath);
- else
- (void)smsg("%2zu %-5" PRId64 " %-34s <none>",
- i, (long)csinfo[i].pid, csinfo[i].fname);
+ if (csinfo[i].ppath != NULL) {
+ (void)smsg("%2zu %-5" PRId64 " %-34s %-32s", i,
+ (int64_t)csinfo[i].pid, csinfo[i].fname, csinfo[i].ppath);
+ } else {
+ (void)smsg("%2zu %-5" PRId64 " %-34s <none>", i,
+ (int64_t)csinfo[i].pid, csinfo[i].fname);
+ }
}
}
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index d3008185dc..f197669a97 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -529,7 +529,7 @@ int get_expr_indent(void)
save_pos = curwin->w_cursor;
save_curswant = curwin->w_curswant;
save_set_curswant = curwin->w_set_curswant;
- set_vim_var_nr(VV_LNUM, curwin->w_cursor.lnum);
+ set_vim_var_nr(VV_LNUM, (varnumber_T) curwin->w_cursor.lnum);
if (use_sandbox) {
sandbox++;
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 340287499e..efe8e73a3c 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -69,23 +69,33 @@ find_start_comment ( /* XXX */
return pos;
}
-/*
- * Find the start of a comment or raw string, not knowing if we are in a
- * comment or raw string right now.
- * Search starts at w_cursor.lnum and goes backwards.
- * Return NULL when not inside a comment or raw string.
- * "CORS" -> Comment Or Raw String
- */
+/// Find the start of a comment or raw string, not knowing if we are in a
+/// comment or raw string right now.
+/// Search starts at w_cursor.lnum and goes backwards.
+///
+/// @returns NULL when not inside a comment or raw string.
+///
+/// @note "CORS" -> Comment Or Raw String
static pos_T *ind_find_start_CORS(void)
-{ /* XXX */
- pos_T *comment_pos = find_start_comment(curbuf->b_ind_maxcomment);
- pos_T *rs_pos = find_start_rawstring(curbuf->b_ind_maxcomment);
-
- /* If comment_pos is before rs_pos the raw string is inside the comment.
- * If rs_pos is before comment_pos the comment is inside the raw string. */
- if (comment_pos == NULL || (rs_pos != NULL && lt(*rs_pos, *comment_pos)))
- return rs_pos;
- return comment_pos;
+{
+ // XXX
+ static pos_T comment_pos_copy;
+
+ pos_T *comment_pos = find_start_comment(curbuf->b_ind_maxcomment);
+ if (comment_pos != NULL) {
+ // Need to make a copy of the static pos in findmatchlimit(),
+ // calling find_start_rawstring() may change it.
+ comment_pos_copy = *comment_pos;
+ comment_pos = &comment_pos_copy;
+ }
+ pos_T *rs_pos = find_start_rawstring(curbuf->b_ind_maxcomment);
+
+ // If comment_pos is before rs_pos the raw string is inside the comment.
+ // If rs_pos is before comment_pos the comment is inside the raw string.
+ if (comment_pos == NULL || (rs_pos != NULL && lt(*rs_pos, *comment_pos))) {
+ return rs_pos;
+ }
+ return comment_pos;
}
/*
@@ -847,13 +857,27 @@ static int cin_isfuncdecl(char_u **sp, linenr_T first_lnum, linenr_T min_lnum)
return FALSE;
while (*s && *s != '(' && *s != ';' && *s != '\'' && *s != '"') {
- if (cin_iscomment(s)) /* ignore comments */
+ // ignore comments
+ if (cin_iscomment(s)) {
s = cin_skipcomment(s);
- else
- ++s;
+ } else if (*s == ':') {
+ if (*(s + 1) == ':') {
+ s += 2;
+ } else {
+ // To avoid a mistake in the following situation:
+ // A::A(int a, int b)
+ // : a(0) // <--not a function decl
+ // , b(0)
+ // {...
+ return false;
+ }
+ } else {
+ s++;
+ }
+ }
+ if (*s != '(') {
+ return false; // ';', ' or " before any () or no '('
}
- if (*s != '(')
- return FALSE; /* ';', ' or " before any () or no '(' */
while (*s && *s != ';' && *s != '\'' && *s != '"') {
if (*s == ')' && cin_nocode(s + 1)) {
@@ -1122,13 +1146,21 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) {
pos->lnum = lnum;
line = ml_get(lnum);
- s = cin_skipcomment(line);
+ s = line;
for (;; ) {
if (*s == NUL) {
- if (lnum == curwin->w_cursor.lnum)
+ if (lnum == curwin->w_cursor.lnum) {
break;
- /* Continue in the cursor line. */
+ }
+ // Continue in the cursor line.
line = ml_get(++lnum);
+ s = line;
+ }
+ if (s == line) {
+ // don't recognize "case (foo):" as a baseclass */
+ if (cin_iscase(s, false)) {
+ break;
+ }
s = cin_skipcomment(line);
if (*s == NUL)
continue;
@@ -2250,15 +2282,14 @@ int get_c_indent(void)
* location for b_ind_open_extra.
*/
- if (start_brace == BRACE_IN_COL0) { /* '{' is in column 0 */
+ if (start_brace == BRACE_IN_COL0) { // '{' is in column 0
amount = curbuf->b_ind_open_left_imag;
- lookfor_cpp_namespace = TRUE;
- } else if (start_brace == BRACE_AT_START &&
- lookfor_cpp_namespace) { /* '{' is at start */
-
- lookfor_cpp_namespace = TRUE;
+ lookfor_cpp_namespace = true;
+ } else if (start_brace == BRACE_AT_START
+ && lookfor_cpp_namespace) { // '{' is at start
+ lookfor_cpp_namespace = true;
} else {
- if (start_brace == BRACE_AT_END) { /* '{' is at end of line */
+ if (start_brace == BRACE_AT_END) { // '{' is at end of line
amount += curbuf->b_ind_open_imag;
l = skipwhite(get_cursor_line_ptr());
@@ -2707,7 +2738,8 @@ int get_c_indent(void)
if (terminated == 0 || (lookfor != LOOKFOR_UNTERM
&& terminated == ',')) {
- if (*skipwhite(l) == '[' || l[STRLEN(l) - 1] == '[') {
+ if (lookfor != LOOKFOR_ENUM_OR_INIT
+ && (*skipwhite(l) == '[' || l[STRLEN(l) - 1] == '[')) {
amount += ind_continuation;
}
// If we're in the middle of a paren thing, Go back to the line
@@ -2915,34 +2947,35 @@ int get_c_indent(void)
continue;
}
- /* Ignore unterminated lines in between, but
- * reduce indent. */
- if (amount > cur_amount)
+ // Ignore unterminated lines in between, but
+ // reduce indent.
+ if (amount > cur_amount) {
amount = cur_amount;
+ }
} else {
- /*
- * Found first unterminated line on a row, may
- * line up with this line, remember its indent
- * 100 +
- * -> here;
- */
+ // Found first unterminated line on a row, may
+ // line up with this line, remember its indent
+ // 100 + // NOLINT(whitespace/tab)
+ // -> here; // NOLINT(whitespace/tab)
l = get_cursor_line_ptr();
amount = cur_amount;
- if (*skipwhite(l) == ']' || l[STRLEN(l) - 1] == ']') {
+
+ n = (int)STRLEN(l);
+ if (terminated == ','
+ && (*skipwhite(l) == ']'
+ || (n >=2 && l[n - 2] == ']'))) {
break;
}
- /*
- * If previous line ends in ',', check whether we
- * are in an initialization or enum
- * struct xxx =
- * {
- * sizeof a,
- * 124 };
- * or a normal possible continuation line.
- * but only, of no other statement has been found
- * yet.
- */
+ // If previous line ends in ',', check whether we
+ // are in an initialization or enum
+ // struct xxx =
+ // {
+ // sizeof a,
+ // 124 };
+ // or a normal possible continuation line.
+ // but only, of no other statement has been found
+ // yet.
if (lookfor == LOOKFOR_INITIAL && terminated == ',') {
if (curbuf->b_ind_js) {
// Search for a line ending in a comma
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index 65c808eb06..99e94fc60f 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -1,8 +1,3 @@
-/*
- * functions that use lookup tables for various things, generally to do with
- * special key codes.
- */
-
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
@@ -39,7 +34,8 @@ static struct modmasktable {
{MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, (char_u)'2'},
{MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, (char_u)'3'},
{MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, (char_u)'4'},
- /* 'A' must be the last one */
+ {MOD_MASK_CMD, MOD_MASK_CMD, (char_u)'D'},
+ // 'A' must be the last one
{MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'A'},
{0, 0, NUL}
};
@@ -486,26 +482,28 @@ char_u *get_special_key_name(int c, int modifiers)
return string;
}
-/*
- * Try translating a <> name at (*srcp)[] to dst[].
- * Return the number of characters added to dst[], zero for no match.
- * If there is a match, srcp is advanced to after the <> name.
- * dst[] must be big enough to hold the result (up to six characters)!
- */
-unsigned int
-trans_special (
- char_u **srcp,
- char_u *dst,
- int keycode /* prefer key code, e.g. K_DEL instead of DEL */
-)
+/// Try translating a <> name
+///
+/// @param[in,out] srcp Source from which <> are translated. Is advanced to
+/// after the <> name if there is a match.
+/// @param[in] src_len Length of the srcp.
+/// @param[out] dst Location where translation result will be kept. Must have
+/// at least six bytes.
+/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL.
+///
+/// @return Number of characters added to dst, zero for no match.
+unsigned int trans_special(const char_u **srcp, const size_t src_len,
+ char_u *const dst, const bool keycode)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
int modifiers = 0;
int key;
unsigned int dlen = 0;
- key = find_special_key(srcp, &modifiers, keycode, FALSE);
- if (key == 0)
+ key = find_special_key(srcp, src_len, &modifiers, keycode, false);
+ if (key == 0) {
return 0;
+ }
/* Put the appropriate modifier in a string */
if (modifiers != 0) {
@@ -518,69 +516,78 @@ trans_special (
dst[dlen++] = K_SPECIAL;
dst[dlen++] = (char_u)KEY2TERMCAP0(key);
dst[dlen++] = KEY2TERMCAP1(key);
- } else if (has_mbyte && !keycode)
+ } else if (has_mbyte && !keycode) {
dlen += (unsigned int)(*mb_char2bytes)(key, dst + dlen);
- else if (keycode) {
+ } else if (keycode) {
char_u *after = add_char2buf(key, dst + dlen);
assert(after >= dst && (uintmax_t)(after - dst) <= UINT_MAX);
dlen = (unsigned int)(after - dst);
- }
- else
+ } else {
dst[dlen++] = (char_u)key;
+ }
return dlen;
}
-// Try translating a <> name at (*srcp)[], return the key and modifiers.
-// srcp is advanced to after the <> name.
-// returns 0 if there is no match.
-int find_special_key(
- char_u **srcp,
- int *modp,
- int keycode, // prefer key code, e.g. K_DEL instead of DEL
- int keep_x_key // don't translate xHome to Home key
-)
+/// Try translating a <> name
+///
+/// @param[in,out] srcp Translated <> name. Is advanced to after the <> name.
+/// @param[in] src_len srcp length.
+/// @param[out] modp Location where information about modifiers is saved.
+/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL.
+/// @param[in] keep_x_key Don’t translate xHome to Home key.
+///
+/// @return Key and modifiers or 0 if there is no match.
+int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
+ const bool keycode, const bool keep_x_key)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- char_u *last_dash;
- char_u *end_of_name;
- char_u *src;
- char_u *bp;
+ const char_u *last_dash;
+ const char_u *end_of_name;
+ const char_u *src;
+ const char_u *bp;
+ const char_u *const end = *srcp + src_len - 1;
int modifiers;
int bit;
int key;
unsigned long n;
int l;
+ if (src_len == 0) {
+ return 0;
+ }
+
src = *srcp;
- if (src[0] != '<')
+ if (src[0] != '<') {
return 0;
+ }
// Find end of modifier list
last_dash = src;
- for (bp = src + 1; *bp == '-' || vim_isIDc(*bp); bp++) {
+ for (bp = src + 1; bp <= end && (*bp == '-' || vim_isIDc(*bp)); bp++) {
if (*bp == '-') {
last_dash = bp;
- if (bp[1] != NUL) {
+ if (bp + 1 <= end) {
if (has_mbyte) {
- l = mb_ptr2len(bp + 1);
+ l = mb_ptr2len_len(bp + 1, (int) (end - bp) + 1);
} else {
l = 1;
}
- if (bp[l + 1] == '>') {
- bp += l; // anything accepted, like <C-?>
+ if (end - bp > l && bp[l + 1] == '>') {
+ bp += l; // anything accepted, like <C-?>
}
}
}
- if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3]) {
- bp += 3; // skip t_xx, xx may be '-' or '>'
- } else if (STRNICMP(bp, "char-", 5) == 0) {
+ if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') {
+ bp += 3; // skip t_xx, xx may be '-' or '>'
+ } else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) {
vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0);
bp += l + 5;
break;
}
}
- if (*bp == '>') { /* found matching '>' */
+ if (bp <= end && *bp == '>') { // found matching '>'
end_of_name = bp + 1;
/* Which modifiers are given? */
@@ -658,9 +665,11 @@ static int extract_modifiers(int key, int *modp)
{
int modifiers = *modp;
- if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) {
- key = TOUPPER_ASC(key);
- modifiers &= ~MOD_MASK_SHIFT;
+ if (!(modifiers & MOD_MASK_CMD)) { // Command-key is special
+ if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) {
+ key = TOUPPER_ASC(key);
+ modifiers &= ~MOD_MASK_SHIFT;
+ }
}
if ((modifiers & MOD_MASK_CTRL)
&& ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) {
@@ -698,7 +707,7 @@ int find_special_key_in_table(int c)
* termcap name.
* Return the key code, or 0 if not found.
*/
-int get_special_key_code(char_u *name)
+int get_special_key_code(const char_u *name)
{
char_u *table_name;
int i, j;
@@ -732,50 +741,58 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag)
return 0; /* Shouldn't get here */
}
-// Replace any terminal code strings in from[] with the equivalent internal
-// vim representation. This is used for the "from" and "to" part of a
-// mapping, and the "to" part of a menu command.
-// Any strings like "<C-UP>" are also replaced, unless 'cpoptions' contains
-// '<'.
-// K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER.
-//
-// The replacement is done in result[] and finally copied into allocated
-// memory. If this all works well *bufp is set to the allocated memory and a
-// pointer to it is returned. If something fails *bufp is set to NULL and from
-// is returned.
-//
-// CTRL-V characters are removed. When "from_part" is TRUE, a trailing CTRL-V
-// is included, otherwise it is removed (for ":map xx ^V", maps xx to
-// nothing). When 'cpoptions' does not contain 'B', a backslash can be used
-// instead of a CTRL-V.
-char_u * replace_termcodes (
- char_u *from,
- char_u **bufp,
- int from_part,
- int do_lt, // also translate <lt>
- int special // always accept <key> notation
-)
+/// Replace any terminal code strings with the equivalent internal
+/// representation
+///
+/// This is used for the "from" and "to" part of a mapping, and the "to" part of
+/// a menu command. Any strings like "<C-UP>" are also replaced, unless
+/// 'cpoptions' contains '<'. K_SPECIAL by itself is replaced by K_SPECIAL
+/// KS_SPECIAL KE_FILLER.
+///
+/// @param[in] from What characters to replace.
+/// @param[in] from_len Length of the "from" argument.
+/// @param[out] bufp Location where results were saved in case of success
+/// (allocated). Will be set to NULL in case of failure.
+/// @param[in] do_lt If true, also translate <lt>.
+/// @param[in] from_part If true, trailing <C-v> is included, otherwise it is
+/// removed (to make ":map xx ^V" map xx to nothing).
+/// When cpo_flags contains #FLAG_CPO_BSLASH, a backslash
+/// can be used in place of <C-v>. All other <C-v>
+/// characters are removed.
+/// @param[in] special If true, always accept <key> notation.
+/// @param[in] cpo_flags Relevant flags derived from p_cpo, see
+/// #CPO_TO_CPO_FLAGS.
+///
+/// @return Pointer to an allocated memory in case of success, "from" in case of
+/// failure. In case of success returned pointer is also saved to
+/// "bufp".
+char_u *replace_termcodes(const char_u *from, const size_t from_len,
+ char_u **bufp, const bool from_part, const bool do_lt,
+ const bool special, int cpo_flags)
+ FUNC_ATTR_NONNULL_ALL
{
ssize_t i;
size_t slen;
char_u key;
size_t dlen = 0;
- char_u *src;
+ const char_u *src;
+ const char_u *const end = from + from_len - 1;
int do_backslash; // backslash is a special character
int do_special; // recognize <> key codes
char_u *result; // buffer for resulting string
- do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
- do_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL) || special;
+ do_backslash = !(cpo_flags&FLAG_CPO_BSLASH);
+ do_special = !(cpo_flags&FLAG_CPO_SPECI) || special;
// Allocate space for the translation. Worst case a single character is
// replaced by 6 bytes (shifted special key), plus a NUL at the end.
- result = xmalloc(STRLEN(from) * 6 + 1);
+ result = xmalloc(from_len * 6 + 1);
src = from;
// Check for #n at start only: function key n
- if (from_part && src[0] == '#' && ascii_isdigit(src[1])) { // function key
+ if (from_part && from_len > 1 && src[0] == '#'
+ && ascii_isdigit(src[1])) { // function key
result[dlen++] = K_SPECIAL;
result[dlen++] = 'k';
if (src[1] == '0') {
@@ -787,13 +804,14 @@ char_u * replace_termcodes (
}
// Copy each byte from *from to result[dlen]
- while (*src != NUL) {
+ while (src <= end) {
// If 'cpoptions' does not contain '<', check for special key codes,
// like "<C-S-LeftMouse>"
- if (do_special && (do_lt || STRNCMP(src, "<lt>", 4) != 0)) {
+ if (do_special && (do_lt || ((end - src) >= 3
+ && STRNCMP(src, "<lt>", 4) != 0))) {
// Replace <SID> by K_SNR <script-nr> _.
// (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14)
- if (STRNICMP(src, "<SID>", 5) == 0) {
+ if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) {
if (current_SID <= 0) {
EMSG(_(e_usingsid));
} else {
@@ -808,7 +826,7 @@ char_u * replace_termcodes (
}
}
- slen = trans_special(&src, result + dlen, TRUE);
+ slen = trans_special(&src, (size_t) (end - src) + 1, result + dlen, true);
if (slen) {
dlen += slen;
continue;
@@ -821,10 +839,10 @@ char_u * replace_termcodes (
// Replace <Leader> by the value of "mapleader".
// Replace <LocalLeader> by the value of "maplocalleader".
// If "mapleader" or "maplocalleader" isn't set use a backslash.
- if (STRNICMP(src, "<Leader>", 8) == 0) {
+ if (end - src >= 7 && STRNICMP(src, "<Leader>", 8) == 0) {
len = 8;
p = get_var_value((char_u *)"g:mapleader");
- } else if (STRNICMP(src, "<LocalLeader>", 13) == 0) {
+ } else if (end - src >= 12 && STRNICMP(src, "<LocalLeader>", 13) == 0) {
len = 13;
p = get_var_value((char_u *)"g:maplocalleader");
} else {
@@ -853,8 +871,8 @@ char_u * replace_termcodes (
// If 'cpoptions' does not contain 'B', also accept a backslash.
key = *src;
if (key == Ctrl_V || (do_backslash && key == '\\')) {
- ++src; // skip CTRL-V or backslash
- if (*src == NUL) {
+ src++; // skip CTRL-V or backslash
+ if (src > end) {
if (from_part) {
result[dlen++] = key;
}
@@ -863,7 +881,7 @@ char_u * replace_termcodes (
}
// skip multibyte char correctly
- for (i = (*mb_ptr2len)(src); i > 0; --i) {
+ for (i = (*mb_ptr2len_len)(src, (int) (end - src) + 1); i > 0; i--) {
// If the character is K_SPECIAL, replace it with K_SPECIAL
// KS_SPECIAL KE_FILLER.
// If compiled with the GUI replace CSI with K_CSI.
diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h
index 766362d145..bb8ba84a6a 100644
--- a/src/nvim/keymap.h
+++ b/src/nvim/keymap.h
@@ -1,6 +1,8 @@
#ifndef NVIM_KEYMAP_H
#define NVIM_KEYMAP_H
+#include "nvim/strings.h"
+
/*
* Keycode definitions for special keys.
*
@@ -112,11 +114,11 @@
#define TO_SPECIAL(a, b) ((a) == KS_SPECIAL ? K_SPECIAL : (a) == \
KS_ZERO ? K_ZERO : TERMCAP2KEY(a, b))
-/*
- * Codes for keys that do not have a termcap name.
- *
- * K_SPECIAL KS_EXTRA KE_xxx
- */
+// Codes for keys that do not have a termcap name.
+//
+// K_SPECIAL KS_EXTRA KE_xxx
+//
+// Entries must be in the range 0x02-0x7f (see comment at K_SPECIAL).
enum key_extra {
KE_NAME = 3 /* name of this terminal entry */
@@ -436,11 +438,12 @@ enum key_extra {
/* 0x01 cannot be used, because the modifier must be 0x02 or higher */
#define MOD_MASK_SHIFT 0x02
#define MOD_MASK_CTRL 0x04
-#define MOD_MASK_ALT 0x08 /* aka META */
-#define MOD_MASK_META 0x10 /* META when it's different from ALT */
-#define MOD_MASK_2CLICK 0x20 /* use MOD_MASK_MULTI_CLICK */
-#define MOD_MASK_3CLICK 0x40 /* use MOD_MASK_MULTI_CLICK */
-#define MOD_MASK_4CLICK 0x60 /* use MOD_MASK_MULTI_CLICK */
+#define MOD_MASK_ALT 0x08 // aka META
+#define MOD_MASK_META 0x10 // META when it's different from ALT
+#define MOD_MASK_2CLICK 0x20 // use MOD_MASK_MULTI_CLICK
+#define MOD_MASK_3CLICK 0x40 // use MOD_MASK_MULTI_CLICK
+#define MOD_MASK_4CLICK 0x60 // use MOD_MASK_MULTI_CLICK
+#define MOD_MASK_CMD 0x80 // "super" key (OSX/Mac: command-key)
#define MOD_MASK_MULTI_CLICK (MOD_MASK_2CLICK|MOD_MASK_3CLICK| \
MOD_MASK_4CLICK)
@@ -451,16 +454,23 @@ enum key_extra {
*/
#define MAX_KEY_NAME_LEN 25
-/* Maximum length of a special key event as tokens. This includes modifiers.
- * The longest event is something like <M-C-S-T-4-LeftDrag> which would be the
- * following string of tokens:
- *
- * <K_SPECIAL> <KS_MODIFIER> bitmask <K_SPECIAL> <KS_EXTRA> <KT_LEFTDRAG>.
- *
- * This is a total of 6 tokens, and is currently the longest one possible.
- */
+// Maximum length of a special key event as tokens. This includes modifiers.
+// The longest event is something like <M-C-S-T-4-LeftDrag> which would be the
+// following string of tokens:
+//
+// <K_SPECIAL> <KS_MODIFIER> bitmask <K_SPECIAL> <KS_EXTRA> <KE_LEFTDRAG>.
+//
+// This is a total of 6 tokens, and is currently the longest one possible.
#define MAX_KEY_CODE_LEN 6
+#define FLAG_CPO_BSLASH 0x01
+#define FLAG_CPO_SPECI 0x02
+#define CPO_TO_CPO_FLAGS (((vim_strchr(p_cpo, CPO_BSLASH) == NULL) \
+ ? 0 \
+ : FLAG_CPO_BSLASH)| \
+ (vim_strchr(p_cpo, CPO_SPECI) == NULL \
+ ? 0 \
+ : FLAG_CPO_SPECI))
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "keymap.h.generated.h"
diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h
index 56be29d14c..8287cb14da 100644
--- a/src/nvim/lib/khash.h
+++ b/src/nvim/lib/khash.h
@@ -184,7 +184,7 @@ typedef khint_t khiter_t;
#define kfree(P) xfree(P)
#endif
-static const double __ac_HASH_UPPER = 0.77;
+#define __ac_HASH_UPPER 0.77
#define __KHASH_TYPE(name, khkey_t, khval_t) \
typedef struct { \
diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h
index 0466cb229c..b41ef0cc9f 100644
--- a/src/nvim/lib/kvec.h
+++ b/src/nvim/lib/kvec.h
@@ -60,6 +60,7 @@ int main() {
#define kv_pop(v) ((v).items[--(v).size])
#define kv_size(v) ((v).size)
#define kv_max(v) ((v).capacity)
+#define kv_last(v) kv_A(v, kv_size(v) - 1)
#define kv_resize(type, v, s) ((v).capacity = (s), (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity))
@@ -77,10 +78,10 @@ int main() {
(v).items[(v).size++] = (x); \
} while (0)
-#define kv_pushp(type, v) (((v).size == (v).capacity)? \
+#define kv_pushp(type, v) ((((v).size == (v).capacity)? \
((v).capacity = ((v).capacity? (v).capacity<<1 : 8), \
(v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity), 0) \
- : 0), ((v).items + ((v).size++))
+ : 0), ((v).items + ((v).size++)))
#define kv_a(type, v, i) (((v).capacity <= (size_t)(i)? \
((v).capacity = (v).size = (i) + 1, kv_roundup32((v).capacity), \
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index 26ab5a7de7..5f69fa2f6a 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -1,6 +1,17 @@
#ifndef NVIM_MACROS_H
#define NVIM_MACROS_H
+// EXTERN is only defined in main.c. That's where global variables are
+// actually defined and initialized.
+#ifndef EXTERN
+# define EXTERN extern
+# define INIT(...)
+#else
+# ifndef INIT
+# define INIT(...) __VA_ARGS__
+# endif
+#endif
+
#ifndef MIN
# define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#endif
diff --git a/src/nvim/main.c b/src/nvim/main.c
index a8c2cebbbd..71a972e8f6 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -238,8 +238,8 @@ int main(int argc, char **argv)
check_and_set_isatty(&params);
// Get the name with which Nvim was invoked, with and without path.
- set_vim_var_string(VV_PROGPATH, (char_u *)argv[0], -1);
- set_vim_var_string(VV_PROGNAME, path_tail((char_u *)argv[0]), -1);
+ set_vim_var_string(VV_PROGPATH, argv[0], -1);
+ set_vim_var_string(VV_PROGNAME, (char *) path_tail((char_u *) argv[0]), -1);
event_init();
/*
@@ -317,14 +317,16 @@ int main(int argc, char **argv)
}
// open terminals when opening files that start with term://
- do_cmdline_cmd("autocmd BufReadCmd term://* "
+#define PROTO "term://"
+ do_cmdline_cmd("autocmd BufReadCmd " PROTO "* nested "
":call termopen( "
// Capture the command string
"matchstr(expand(\"<amatch>\"), "
- "'\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), "
+ "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), "
// capture the working directory
"{'cwd': get(matchlist(expand(\"<amatch>\"), "
- "'\\c\\mterm://\\(.\\{-}\\)//'), 1, '')})");
+ "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, '')})");
+#undef PROTO
/* Execute --cmd arguments. */
exe_pre_commands(&params);
@@ -332,6 +334,14 @@ int main(int argc, char **argv)
/* Source startup scripts. */
source_startup_scripts(&params);
+ // If using the runtime (-u is not NONE), enable syntax & filetype plugins.
+ if (params.use_vimrc == NULL || strcmp(params.use_vimrc, "NONE") != 0) {
+ // Does ":filetype plugin indent on".
+ filetype_maybe_enable();
+ // Sources syntax/syntax.vim, which calls `:filetype on`.
+ syn_maybe_on();
+ }
+
/*
* Read all the plugin files.
* Only when compiled with +eval, since most plugins need it.
@@ -649,6 +659,9 @@ static void init_locale(void)
setlocale(LC_NUMERIC, "C");
# endif
+# ifdef LOCALE_INSTALL_DIR // gnu/linux standard: $prefix/share/locale
+ bindtextdomain(PROJECT_NAME, LOCALE_INSTALL_DIR);
+# else // old vim style: $runtime/lang
{
char_u *p;
@@ -657,11 +670,12 @@ static void init_locale(void)
p = (char_u *)vim_getenv("VIMRUNTIME");
if (p != NULL && *p != NUL) {
vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p);
- bindtextdomain(VIMPACKAGE, (char *)NameBuff);
+ bindtextdomain(PROJECT_NAME, (char *)NameBuff);
}
xfree(p);
- textdomain(VIMPACKAGE);
}
+# endif
+ textdomain(PROJECT_NAME);
TIME_MSG("locale set");
}
#endif
@@ -739,6 +753,7 @@ static void command_line_scan(mparm_T *parmp)
putchar(b->data[i]);
}
+ msgpack_packer_free(p);
mch_exit(0);
} else if (STRICMP(argv[0] + argv_idx, "headless") == 0) {
parmp->headless = true;
@@ -1126,10 +1141,11 @@ scripterror:
/* If there is a "+123" or "-c" command, set v:swapcommand to the first
* one. */
if (parmp->n_commands > 0) {
- p = xmalloc(STRLEN(parmp->commands[0]) + 3);
- sprintf((char *)p, ":%s\r", parmp->commands[0]);
- set_vim_var_string(VV_SWAPCOMMAND, p, -1);
- xfree(p);
+ const size_t swcmd_len = STRLEN(parmp->commands[0]) + 3;
+ char *const swcmd = xmalloc(swcmd_len);
+ snprintf(swcmd, swcmd_len, ":%s\r", parmp->commands[0]);
+ set_vim_var_string(VV_SWAPCOMMAND, swcmd, -1);
+ xfree(swcmd);
}
TIME_MSG("parsing arguments");
}
diff --git a/src/nvim/map.c b/src/nvim/map.c
index ed7bda4cce..d4262ae9a8 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -18,6 +18,9 @@
#define uint32_t_eq kh_int_hash_equal
#define int_hash kh_int_hash_func
#define int_eq kh_int_hash_equal
+#define linenr_T_hash kh_int_hash_func
+#define linenr_T_eq kh_int_hash_equal
+
#if defined(ARCH_64)
#define ptr_t_hash(key) uint64_t_hash((uint64_t)key)
@@ -78,6 +81,25 @@
return rv; \
} \
\
+ U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put) \
+ { \
+ int ret; \
+ khiter_t k; \
+ if (put) { \
+ k = kh_put(T##_##U##_map, map->table, key, &ret); \
+ if (ret) { \
+ kh_val(map->table, k) = INITIALIZER(T, U); \
+ } \
+ } else { \
+ k = kh_get(T##_##U##_map, map->table, key); \
+ if (k == kh_end(map->table)) { \
+ return NULL; \
+ } \
+ } \
+ \
+ return &kh_val(map->table, k); \
+ } \
+ \
U map_##T##_##U##_del(Map(T, U) *map, T key) \
{ \
U rv = INITIALIZER(T, U); \
@@ -118,3 +140,5 @@ MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER)
#define MSGPACK_HANDLER_INITIALIZER {.fn = NULL, .async = false}
MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER)
+#define KVEC_INITIALIZER { .size = 0, .capacity = 0, .items = NULL }
+MAP_IMPL(linenr_T, bufhl_vec_T, KVEC_INITIALIZER)
diff --git a/src/nvim/map.h b/src/nvim/map.h
index c0e2ca3aac..e90cc360ce 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -6,6 +6,7 @@
#include "nvim/map_defs.h"
#include "nvim/api/private/defs.h"
#include "nvim/msgpack_rpc/defs.h"
+#include "nvim/bufhl_defs.h"
#define MAP_DECLS(T, U) \
KHASH_DECLARE(T##_##U##_map, T, U) \
@@ -19,6 +20,7 @@
U map_##T##_##U##_get(Map(T, U) *map, T key); \
bool map_##T##_##U##_has(Map(T, U) *map, T key); \
U map_##T##_##U##_put(Map(T, U) *map, T key, U value); \
+ U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put); \
U map_##T##_##U##_del(Map(T, U) *map, T key); \
void map_##T##_##U##_clear(Map(T, U) *map);
@@ -28,12 +30,14 @@ MAP_DECLS(cstr_t, ptr_t)
MAP_DECLS(ptr_t, ptr_t)
MAP_DECLS(uint64_t, ptr_t)
MAP_DECLS(String, MsgpackRpcRequestHandler)
+MAP_DECLS(linenr_T, bufhl_vec_T)
#define map_new(T, U) map_##T##_##U##_new
#define map_free(T, U) map_##T##_##U##_free
#define map_get(T, U) map_##T##_##U##_get
#define map_has(T, U) map_##T##_##U##_has
#define map_put(T, U) map_##T##_##U##_put
+#define map_ref(T, U) map_##T##_##U##_ref
#define map_del(T, U) map_##T##_##U##_del
#define map_clear(T, U) map_##T##_##U##_clear
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index e2f212340c..fe802e48ba 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -922,6 +922,7 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
}
sign_mark_adjust(line1, line2, amount, amount_after);
+ bufhl_mark_adjust(curbuf, line1, line2, amount, amount_after);
}
/* previous context mark */
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index fdd83f9dac..3495203c43 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -571,8 +571,8 @@ char_u * mb_init(void)
#ifdef HAVE_WORKING_LIBINTL
/* GNU gettext 0.10.37 supports this feature: set the codeset used for
* translated messages independently from the current locale. */
- (void)bind_textdomain_codeset(VIMPACKAGE,
- enc_utf8 ? "utf-8" : (char *)p_enc);
+ (void)bind_textdomain_codeset(PROJECT_NAME,
+ enc_utf8 ? "utf-8" : (char *)p_enc);
#endif
@@ -1304,35 +1304,38 @@ int utfc_ptr2char(const char_u *p, int *pcc)
*/
int utfc_ptr2char_len(const char_u *p, int *pcc, int maxlen)
{
- int len;
- int c;
- int cc;
+#define IS_COMPOSING(s1, s2, s3) \
+ (i == 0 ? UTF_COMPOSINGLIKE((s1), (s2)) : utf_iscomposing((s3)))
+
+ assert(maxlen > 0);
+
int i = 0;
- c = utf_ptr2char(p);
- len = utf_ptr2len_len(p, maxlen);
- /* Only accept a composing char when the first char isn't illegal. */
- if ((len > 1 || *p < 0x80)
- && len < maxlen
- && p[len] >= 0x80
- && UTF_COMPOSINGLIKE(p, p + len)) {
- cc = utf_ptr2char(p + len);
- for (;; ) {
- pcc[i++] = cc;
- if (i == MAX_MCO)
- break;
- len += utf_ptr2len_len(p + len, maxlen - len);
- if (len >= maxlen
- || p[len] < 0x80
- || !utf_iscomposing(cc = utf_ptr2char(p + len)))
+ int len = utf_ptr2len_len(p, maxlen);
+ // Is it safe to use utf_ptr2char()?
+ bool safe = len > 1 && len <= maxlen;
+ int c = safe ? utf_ptr2char(p) : *p;
+
+ // Only accept a composing char when the first char isn't illegal.
+ if ((safe || c < 0x80) && len < maxlen && p[len] >= 0x80) {
+ for (; i < MAX_MCO; i++) {
+ int len_cc = utf_ptr2len_len(p + len, maxlen - len);
+ safe = len_cc > 1 && len_cc <= maxlen - len;
+ if (!safe || (pcc[i] = utf_ptr2char(p + len)) < 0x80
+ || !IS_COMPOSING(p, p + len, pcc[i])) {
break;
+ }
+ len += len_cc;
}
}
- if (i < MAX_MCO) /* last composing char must be 0 */
+ if (i < MAX_MCO) {
+ // last composing char must be 0
pcc[i] = 0;
+ }
return c;
+#undef ISCOMPOSING
}
/*
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index 8cf5642a80..9fb03c4ac7 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -79,8 +79,6 @@ static size_t total_mem_used = 0; /// total memory used for memfiles
/// - NULL, on failure.
memfile_T *mf_open(char_u *fname, int flags)
{
- off_t size;
-
memfile_T *mfp = xmalloc(sizeof(memfile_T));
if (fname == NULL) { // no file, use memory only
@@ -88,11 +86,9 @@ memfile_T *mf_open(char_u *fname, int flags)
mfp->mf_ffname = NULL;
mfp->mf_fd = -1;
} else { // try to open the file
- mf_do_open(mfp, fname, flags);
-
- if (mfp->mf_fd < 0) { // fail if file could not be opened
+ if (!mf_do_open(mfp, fname, flags)) {
xfree(mfp);
- return NULL;
+ return NULL; // fail if file could not be opened
}
}
@@ -115,6 +111,8 @@ memfile_T *mf_open(char_u *fname, int flags)
}
}
+ off_t size;
+
// When recovering, the actual block size will be retrieved from block 0
// in ml_recover(). The size used here may be wrong, therefore mf_blocknr_max
// must be rounded up.
@@ -171,13 +169,12 @@ memfile_T *mf_open(char_u *fname, int flags)
/// FAIL If file could not be opened.
int mf_open_file(memfile_T *mfp, char_u *fname)
{
- mf_do_open(mfp, fname, O_RDWR|O_CREAT|O_EXCL); // try to open the file
-
- if (mfp->mf_fd < 0)
- return FAIL;
+ if (mf_do_open(mfp, fname, O_RDWR | O_CREAT | O_EXCL)) {
+ mfp->mf_dirty = true;
+ return OK;
+ }
- mfp->mf_dirty = true;
- return OK;
+ return FAIL;
}
/// Close a memory file and optionally delete the associated file.
@@ -185,28 +182,28 @@ int mf_open_file(memfile_T *mfp, char_u *fname)
/// @param del_file Whether to delete associated file.
void mf_close(memfile_T *mfp, bool del_file)
{
- bhdr_T *hp, *nextp;
-
if (mfp == NULL) { // safety check
return;
}
if (mfp->mf_fd >= 0 && close(mfp->mf_fd) < 0) {
EMSG(_(e_swapclose));
}
- if (del_file && mfp->mf_fname != NULL)
+ if (del_file && mfp->mf_fname != NULL) {
os_remove((char *)mfp->mf_fname);
+ }
+
// free entries in used list
- for (hp = mfp->mf_used_first; hp != NULL; hp = nextp) {
+ for (bhdr_T *hp = mfp->mf_used_first, *nextp; hp != NULL; hp = nextp) {
total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
nextp = hp->bh_next;
mf_free_bhdr(hp);
}
- while (mfp->mf_free_first != NULL) // free entries in free list
+ while (mfp->mf_free_first != NULL) { // free entries in free list
xfree(mf_rem_free(mfp));
+ }
mf_hash_free(&mfp->mf_hash);
mf_hash_free_all(&mfp->mf_trans); // free hashtable and its items
- xfree(mfp->mf_fname);
- xfree(mfp->mf_ffname);
+ mf_free_fnames(mfp);
xfree(mfp);
}
@@ -216,28 +213,28 @@ void mf_close(memfile_T *mfp, bool del_file)
void mf_close_file(buf_T *buf, bool getlines)
{
memfile_T *mfp = buf->b_ml.ml_mfp;
- if (mfp == NULL || mfp->mf_fd < 0) // nothing to close
+ if (mfp == NULL || mfp->mf_fd < 0) { // nothing to close
return;
+ }
if (getlines) {
// get all blocks in memory by accessing all lines (clumsy!)
mf_dont_release = true;
- for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum)
+ for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) {
(void)ml_get_buf(buf, lnum, false);
+ }
mf_dont_release = false;
// TODO(elmart): should check if all blocks are really in core
}
- if (close(mfp->mf_fd) < 0) // close the file
+ if (close(mfp->mf_fd) < 0) { // close the file
EMSG(_(e_swapclose));
+ }
mfp->mf_fd = -1;
if (mfp->mf_fname != NULL) {
os_remove((char *)mfp->mf_fname); // delete the swap file
- xfree(mfp->mf_fname);
- xfree(mfp->mf_ffname);
- mfp->mf_fname = NULL;
- mfp->mf_ffname = NULL;
+ mf_free_fnames(mfp);
}
}
@@ -390,11 +387,11 @@ void mf_put(memfile_T *mfp, bhdr_T *hp, bool dirty, bool infile)
/// Signal block as no longer used (may put it in the free list).
void mf_free(memfile_T *mfp, bhdr_T *hp)
{
- xfree(hp->bh_data); // free data
+ xfree(hp->bh_data); // free data
mf_rem_hash(mfp, hp); // get *hp out of the hash list
mf_rem_used(mfp, hp); // get *hp out of the used list
if (hp->bh_bnum < 0) {
- xfree(hp); // don't want negative numbers in free list
+ xfree(hp); // don't want negative numbers in free list
mfp->mf_neg_count--;
} else {
mf_ins_free(mfp, hp); // put *hp in the free list
@@ -475,10 +472,11 @@ int mf_sync(memfile_T *mfp, int flags)
/// These are blocks that need to be written to a newly created swapfile.
void mf_set_dirty(memfile_T *mfp)
{
- bhdr_T *hp;
- for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
- if (hp->bh_bnum > 0)
+ for (bhdr_T *hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) {
+ if (hp->bh_bnum > 0) {
hp->bh_flags |= BH_DIRTY;
+ }
+ }
mfp->mf_dirty = true;
}
@@ -506,10 +504,11 @@ static void mf_ins_used(memfile_T *mfp, bhdr_T *hp)
hp->bh_next = mfp->mf_used_first;
mfp->mf_used_first = hp;
hp->bh_prev = NULL;
- if (hp->bh_next == NULL) // list was empty, adjust last pointer
+ if (hp->bh_next == NULL) { // list was empty, adjust last pointer
mfp->mf_used_last = hp;
- else
+ } else {
hp->bh_next->bh_prev = hp;
+ }
mfp->mf_used_count += hp->bh_page_count;
total_mem_used += hp->bh_page_count * mfp->mf_page_size;
}
@@ -615,9 +614,10 @@ bool mf_release_all(void)
FOR_ALL_BUFFERS(buf) {
memfile_T *mfp = buf->b_ml.ml_mfp;
if (mfp != NULL) {
- // If no swap file yet, may open one.
- if (mfp->mf_fd < 0 && buf->b_may_swap)
+ // If no swap file yet, try to open one.
+ if (mfp->mf_fd < 0 && buf->b_may_swap) {
ml_open_file(buf);
+ }
// Flush as many blocks as possible, only if there is a swapfile.
if (mfp->mf_fd >= 0) {
@@ -752,7 +752,8 @@ static int mf_write(memfile_T *mfp, bhdr_T *hp)
else
page_count = hp2->bh_page_count;
size = page_size * page_count;
- if (mf_write_block(mfp, hp2 == NULL ? hp : hp2, offset, size) == FAIL) {
+ void *data = (hp2 == NULL) ? hp->bh_data : hp2->bh_data;
+ if ((unsigned)write_eintr(mfp->mf_fd, data, size) != size) {
/// Avoid repeating the error message, this mostly happens when the
/// disk is full. We give the message again only after a successful
/// write or when hitting a key. We keep on trying, in case some
@@ -773,20 +774,6 @@ static int mf_write(memfile_T *mfp, bhdr_T *hp)
return OK;
}
-/// Write block to memfile's file.
-///
-/// @return OK On success.
-/// FAIL On failure.
-static int mf_write_block(memfile_T *mfp, bhdr_T *hp,
- off_t offset, unsigned size)
-{
- void *data = hp->bh_data;
- int result = OK;
- if ((unsigned)write_eintr(mfp->mf_fd, data, size) != size)
- result = FAIL;
- return result;
-}
-
/// Make block number positive and add it to the translation list.
///
/// @return OK On success.
@@ -856,13 +843,23 @@ blocknr_T mf_trans_del(memfile_T *mfp, blocknr_T old_nr)
return new_bnum;
}
-/// Set full file name of memfile's swapfile, out of simple file name and some
-/// other considerations.
+/// Frees mf_fname and mf_ffname.
+void mf_free_fnames(memfile_T *mfp)
+{
+ xfree(mfp->mf_fname);
+ xfree(mfp->mf_ffname);
+ mfp->mf_fname = NULL;
+ mfp->mf_ffname = NULL;
+}
+
+/// Set the simple file name and the full file name of memfile's swapfile, out
+/// of simple file name and some other considerations.
///
/// Only called when creating or renaming the swapfile. Either way it's a new
/// name so we must work out the full path name.
-void mf_set_ffname(memfile_T *mfp)
+void mf_set_fnames(memfile_T *mfp, char_u *fname)
{
+ mfp->mf_fname = fname;
mfp->mf_ffname = (char_u *)FullName_save((char *)mfp->mf_fname, false);
}
@@ -878,7 +875,7 @@ void mf_fullname(memfile_T *mfp)
}
}
-/// Return TRUE if there are any translations pending for memfile.
+/// Return true if there are any translations pending for memfile.
bool mf_need_trans(memfile_T *mfp)
{
return mfp->mf_fname != NULL && mfp->mf_neg_count > 0;
@@ -889,11 +886,11 @@ bool mf_need_trans(memfile_T *mfp)
/// "fname" must be in allocated memory, and is consumed (also when error).
///
/// @param flags Flags for open().
-static void mf_do_open(memfile_T *mfp, char_u *fname, int flags)
+/// @return A bool indicating success of the `open` call.
+static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags)
{
// fname cannot be NameBuff, because it must have been allocated.
- mfp->mf_fname = fname;
- mf_set_ffname(mfp);
+ mf_set_fnames(mfp, fname);
/// Extra security check: When creating a swap file it really shouldn't
/// exist yet. If there is a symbolic link, this is most likely an attack.
@@ -904,26 +901,26 @@ static void mf_do_open(memfile_T *mfp, char_u *fname, int flags)
EMSG(_("E300: Swap file already exists (symlink attack?)"));
} else {
// try to open the file
- flags |= O_NOFOLLOW;
- mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags);
+ mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags | O_NOFOLLOW);
}
// If the file cannot be opened, use memory only
if (mfp->mf_fd < 0) {
- xfree(mfp->mf_fname);
- xfree(mfp->mf_ffname);
- mfp->mf_fname = NULL;
- mfp->mf_ffname = NULL;
- } else {
+ mf_free_fnames(mfp);
+ return false;
+ }
+
#ifdef HAVE_FD_CLOEXEC
- int fdflags = fcntl(mfp->mf_fd, F_GETFD);
- if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
- fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
+ int fdflags = fcntl(mfp->mf_fd, F_GETFD);
+ if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) {
+ (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
+ }
#endif
#ifdef HAVE_SELINUX
- mch_copy_sec(fname, mfp->mf_fname);
+ mch_copy_sec(fname, mfp->mf_fname);
#endif
- }
+
+ return true;
}
//
@@ -948,20 +945,21 @@ static void mf_hash_init(mf_hashtab_T *mht)
/// The hash table must not be used again without another mf_hash_init() call.
static void mf_hash_free(mf_hashtab_T *mht)
{
- if (mht->mht_buckets != mht->mht_small_buckets)
+ if (mht->mht_buckets != mht->mht_small_buckets) {
xfree(mht->mht_buckets);
+ }
}
/// Free the array of a hash table and all the items it contains.
static void mf_hash_free_all(mf_hashtab_T *mht)
{
- mf_hashitem_T *next;
-
- for (size_t idx = 0; idx <= mht->mht_mask; idx++)
+ for (size_t idx = 0; idx <= mht->mht_mask; idx++) {
+ mf_hashitem_T *next;
for (mf_hashitem_T *mhi = mht->mht_buckets[idx]; mhi != NULL; mhi = next) {
next = mhi->mhi_next;
xfree(mhi);
}
+ }
mf_hash_free(mht);
}
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index f58b2ac38f..9c20f15727 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -65,7 +65,6 @@
#include "nvim/strings.h"
#include "nvim/ui.h"
#include "nvim/version.h"
-#include "nvim/tempfile.h"
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
@@ -426,10 +425,8 @@ void ml_setname(buf_T *buf)
/* try to rename the swap file */
if (vim_rename(mfp->mf_fname, fname) == 0) {
success = TRUE;
- xfree(mfp->mf_fname);
- mfp->mf_fname = fname;
- xfree(mfp->mf_ffname);
- mf_set_ffname(mfp);
+ mf_free_fnames(mfp);
+ mf_set_fnames(mfp, fname);
ml_upd_block0(buf, UB_SAME_DIR);
break;
}
@@ -446,8 +443,9 @@ void ml_setname(buf_T *buf)
#ifdef HAVE_FD_CLOEXEC
{
int fdflags = fcntl(mfp->mf_fd, F_GETFD);
- if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
- fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
+ if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) {
+ (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
+ }
}
#endif
}
@@ -789,9 +787,8 @@ void ml_recover(void)
if (fname == NULL) /* When there is no file name */
fname = (char_u *)"";
len = (int)STRLEN(fname);
- if (len >= 4 &&
- STRNICMP(fname + len - 4, ".s", 2)
- == 0
+ if (len >= 4
+ && STRNICMP(fname + len - 4, ".s", 2) == 0
&& vim_strchr((char_u *)"UVWuvw", fname[len - 2]) != NULL
&& ASCII_ISALPHA(fname[len - 1])) {
directly = TRUE;
@@ -3196,7 +3193,7 @@ attention_message (
*/
static int do_swapexists(buf_T *buf, char_u *fname)
{
- set_vim_var_string(VV_SWAPNAME, fname, -1);
+ set_vim_var_string(VV_SWAPNAME, (char *) fname, -1);
set_vim_var_string(VV_SWAPCHOICE, NULL, -1);
/* Trigger SwapExists autocommands with <afile> set to the file being
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 91a72abfc5..3c2394d579 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -215,10 +215,12 @@ ex_menu (
if (STRICMP(map_to, "<nop>") == 0) { /* "<Nop>" means nothing */
map_to = (char_u *)"";
map_buf = NULL;
- } else if (modes & MENU_TIP_MODE)
- map_buf = NULL; /* Menu tips are plain text. */
- else
- map_to = replace_termcodes(map_to, &map_buf, FALSE, TRUE, special);
+ } else if (modes & MENU_TIP_MODE) {
+ map_buf = NULL; // Menu tips are plain text.
+ } else {
+ map_to = replace_termcodes(map_to, STRLEN(map_to), &map_buf, false, true,
+ special, CPO_TO_CPO_FLAGS);
+ }
menuarg.modes = modes;
menuarg.noremap[0] = noremap;
menuarg.silent[0] = silent;
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 1dd71baaa4..521db85cf0 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -61,14 +61,8 @@ static int confirm_msg_used = FALSE; /* displaying confirm_msg */
static char_u *confirm_msg = NULL; /* ":confirm" message */
static char_u *confirm_msg_tail; /* tail of confirm_msg */
-struct msg_hist {
- struct msg_hist *next;
- char_u *msg;
- int attr;
-};
-
-static struct msg_hist *first_msg_hist = NULL;
-static struct msg_hist *last_msg_hist = NULL;
+MessageHistoryEntry *first_msg_hist = NULL;
+MessageHistoryEntry *last_msg_hist = NULL;
static int msg_hist_len = 0;
static FILE *verbose_fd = NULL;
@@ -149,10 +143,11 @@ msg_attr_keep (
{
static int entered = 0;
int retval;
- char_u *buf = NULL;
+ char_u *buf = NULL;
- if (attr == 0)
- set_vim_var_string(VV_STATUSMSG, s, -1);
+ if (attr == 0) {
+ set_vim_var_string(VV_STATUSMSG, (char *) s, -1);
+ }
/*
* It is possible that displaying a messages causes a problem (e.g.,
@@ -472,22 +467,23 @@ int emsg(char_u *s)
{
int attr;
char_u *p;
- int ignore = FALSE;
+ int ignore = false;
int severe;
- /* Skip this if not giving error messages at the moment. */
- if (emsg_not_now())
- return TRUE;
+ // Skip this if not giving error messages at the moment.
+ if (emsg_not_now()) {
+ return true;
+ }
- called_emsg = TRUE;
- ex_exitval = 1;
+ called_emsg = true;
+ if (emsg_silent == 0) {
+ ex_exitval = 1;
+ }
- /*
- * If "emsg_severe" is TRUE: When an error exception is to be thrown,
- * prefer this message over previous messages for the same command.
- */
+ // If "emsg_severe" is TRUE: When an error exception is to be thrown,
+ // prefer this message over previous messages for the same command.
severe = emsg_severe;
- emsg_severe = FALSE;
+ emsg_severe = false;
if (!emsg_off || vim_strchr(p_debug, 't') != NULL) {
/*
@@ -503,8 +499,8 @@ int emsg(char_u *s)
return TRUE;
}
- /* set "v:errmsg", also when using ":silent! cmd" */
- set_vim_var_string(VV_ERRMSG, s, -1);
+ // set "v:errmsg", also when using ":silent! cmd"
+ set_vim_var_string(VV_ERRMSG, (char *) s, -1);
/*
* When using ":silent! cmd" ignore error messages.
@@ -563,49 +559,23 @@ int emsg(char_u *s)
return msg_attr(s, attr);
}
-/*
- * Print an error message with one "%s" and one string argument.
- */
-int emsg2(char_u *s, char_u *a1)
-{
- return emsg3(s, a1, NULL);
-}
-
void emsg_invreg(int name)
{
EMSG2(_("E354: Invalid register name: '%s'"), transchar(name));
}
-/// Print an error message with one or two "%s" and one or two string arguments.
-int emsg3(char_u *s, char_u *a1, char_u *a2)
-{
- if (emsg_not_now()) {
- return TRUE; // no error messages at the moment
- }
-
- vim_snprintf((char *)IObuff, IOSIZE, (char *)s, a1, a2);
- return emsg(IObuff);
-}
-
-/// Print an error message with one "%" PRId64 and one (int64_t) argument.
-int emsgn(char_u *s, int64_t n)
+/// Print an error message with unknown number of arguments
+bool emsgf(const char *const fmt, ...)
{
if (emsg_not_now()) {
- return TRUE; // no error messages at the moment
+ return true;
}
- vim_snprintf((char *)IObuff, IOSIZE, (char *)s, n);
- return emsg(IObuff);
-}
-
-/// Print an error message with one "%" PRIu64 and one (uint64_t) argument.
-int emsgu(char_u *s, uint64_t n)
-{
- if (emsg_not_now()) {
- return TRUE; // no error messages at the moment
- }
+ va_list ap;
+ va_start(ap, fmt);
+ vim_vsnprintf((char *) IObuff, IOSIZE, fmt, ap, NULL);
+ va_end(ap);
- vim_snprintf((char *)IObuff, IOSIZE, (char *)s, n);
return emsg(IObuff);
}
@@ -815,11 +785,13 @@ void wait_return(int redraw)
State = HITRETURN;
setmouse();
- /* Avoid the sequence that the user types ":" at the hit-return prompt
- * to start an Ex command, but the file-changed dialog gets in the
- * way. */
- if (need_check_timestamps)
- check_timestamps(FALSE);
+ cmdline_row = msg_row;
+ // Avoid the sequence that the user types ":" at the hit-return prompt
+ // to start an Ex command, but the file-changed dialog gets in the
+ // way.
+ if (need_check_timestamps) {
+ check_timestamps(false);
+ }
hit_return_msg();
@@ -1538,51 +1510,44 @@ void msg_puts_attr(char_u *s, int attr)
msg_puts_attr_len(s, -1, attr);
}
-/*
- * Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes).
- * When "maxlen" is -1 there is no maximum length.
- * When "maxlen" is >= 0 the message is not put in the history.
- */
+/// Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes).
+/// When "maxlen" is -1 there is no maximum length.
+/// When "maxlen" is >= 0 the message is not put in the history.
static void msg_puts_attr_len(char_u *str, int maxlen, int attr)
{
- /*
- * If redirection is on, also write to the redirection file.
- */
+ // If redirection is on, also write to the redirection file.
redir_write(str, maxlen);
- /*
- * Don't print anything when using ":silent cmd".
- */
- if (msg_silent != 0)
+ // Don't print anything when using ":silent cmd".
+ if (msg_silent != 0) {
return;
+ }
- /* if MSG_HIST flag set, add message to history */
+ // if MSG_HIST flag set, add message to history
if ((attr & MSG_HIST) && maxlen < 0) {
add_msg_hist(str, -1, attr);
attr &= ~MSG_HIST;
}
- /*
- * When writing something to the screen after it has scrolled, requires a
- * wait-return prompt later. Needed when scrolling, resetting
- * need_wait_return after some prompt, and then outputting something
- * without scrolling
- */
- if (msg_scrolled != 0 && !msg_scrolled_ign)
- need_wait_return = TRUE;
- msg_didany = TRUE; /* remember that something was outputted */
+ // When writing something to the screen after it has scrolled, requires a
+ // wait-return prompt later. Needed when scrolling, resetting
+ // need_wait_return after some prompt, and then outputting something
+ // without scrolling
+ if (msg_scrolled != 0 && !msg_scrolled_ign) {
+ need_wait_return = true;
+ }
+ msg_didany = true; // remember that something was outputted
- /*
- * If there is no valid screen, use fprintf so we can see error messages.
- * If termcap is not active, we may be writing in an alternate console
- * window, cursor positioning may not work correctly (window size may be
- * different, e.g. for Win32 console) or we just don't know where the
- * cursor is.
- */
- if (msg_use_printf())
- msg_puts_printf(str, maxlen);
- else
- msg_puts_display(str, maxlen, attr, FALSE);
+ // If there is no valid screen, use fprintf so we can see error messages.
+ // If termcap is not active, we may be writing in an alternate console
+ // window, cursor positioning may not work correctly (window size may be
+ // different, e.g. for Win32 console) or we just don't know where the
+ // cursor is.
+ if (msg_use_printf()) {
+ msg_puts_printf((char *)str, maxlen);
+ } else {
+ msg_puts_display(str, maxlen, attr, false);
+ }
}
/*
@@ -1601,39 +1566,31 @@ static void msg_puts_display(char_u *str, int maxlen, int attr, int recurse)
int wrap;
int did_last_char;
- did_wait_return = FALSE;
+ did_wait_return = false;
while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) {
- /*
- * We are at the end of the screen line when:
- * - When outputting a newline.
- * - When outputting a character in the last column.
- */
- if (!recurse && msg_row >= Rows - 1 && (*s == '\n' || (
- cmdmsg_rl
- ? (
- msg_col <= 1
- || (*s == TAB && msg_col <= 7)
- || (has_mbyte &&
- (*mb_ptr2cells)(s) > 1 &&
- msg_col <= 2)
- )
- :
- (msg_col + t_col >= Columns - 1
- || (*s == TAB && msg_col +
- t_col >= ((Columns - 1) & ~7))
- || (has_mbyte &&
- (*mb_ptr2cells)(s) > 1
- && msg_col + t_col >=
- Columns - 2)
- )))) {
- /*
- * The screen is scrolled up when at the last row (some terminals
- * scroll automatically, some don't. To avoid problems we scroll
- * ourselves).
- */
- if (t_col > 0)
- /* output postponed text */
+ // We are at the end of the screen line when:
+ // - When outputting a newline.
+ // - When outputting a character in the last column.
+ if (!recurse && msg_row >= Rows - 1
+ && (*s == '\n' || (cmdmsg_rl
+ ? (msg_col <= 1
+ || (*s == TAB && msg_col <= 7)
+ || (has_mbyte
+ && (*mb_ptr2cells)(s) > 1
+ && msg_col <= 2))
+ : (msg_col + t_col >= Columns - 1
+ || (*s == TAB
+ && msg_col + t_col >= ((Columns - 1) & ~7))
+ || (has_mbyte
+ && (*mb_ptr2cells)(s) > 1
+ && msg_col + t_col >= Columns - 2))))) {
+ // The screen is scrolled up when at the last row (some terminals
+ // scroll automatically, some don't. To avoid problems we scroll
+ // ourselves).
+ if (t_col > 0) {
+ // output postponed text
t_puts(&t_col, t_s, s, attr);
+ }
/* When no more prompt and no more room, truncate here */
if (msg_no_more && lines_left == 0)
@@ -1740,18 +1697,15 @@ static void msg_puts_display(char_u *str, int maxlen, int attr, int recurse)
cw = 1;
l = 1;
}
- /* When drawing from right to left or when a double-wide character
- * doesn't fit, draw a single character here. Otherwise collect
- * characters and draw them all at once later. */
- if (
- cmdmsg_rl
- ||
- (cw > 1 && msg_col + t_col >= Columns - 1)
- ) {
- if (l > 1)
+ // When drawing from right to left or when a double-wide character
+ // doesn't fit, draw a single character here. Otherwise collect
+ // characters and draw them all at once later.
+ if (cmdmsg_rl || (cw > 1 && msg_col + t_col >= Columns - 1)) {
+ if (l > 1) {
s = screen_puts_mbyte(s, l, attr) - 1;
- else
+ } else {
msg_screen_putchar(*s, attr);
+ }
} else {
/* postpone this character until later */
if (t_col == 0)
@@ -1787,25 +1741,24 @@ static void msg_scroll_up(void)
static void inc_msg_scrolled(void)
{
if (*get_vim_var_str(VV_SCROLLSTART) == NUL) {
- char_u *p = sourcing_name;
- char_u *tofree = NULL;
- int len;
-
- /* v:scrollstart is empty, set it to the script/function name and line
- * number */
- if (p == NULL)
- p = (char_u *)_("Unknown");
- else {
- len = (int)STRLEN(p) + 40;
+ char *p = (char *) sourcing_name;
+ char *tofree = NULL;
+
+ // v:scrollstart is empty, set it to the script/function name and line
+ // number
+ if (p == NULL) {
+ p = _("Unknown");
+ } else {
+ size_t len = strlen(p) + 40;
tofree = xmalloc(len);
- vim_snprintf((char *)tofree, len, _("%s line %" PRId64),
- p, (int64_t)sourcing_lnum);
+ vim_snprintf(tofree, len, _("%s line %" PRId64),
+ p, (int64_t) sourcing_lnum);
p = tofree;
}
set_vim_var_string(VV_SCROLLSTART, p, -1);
xfree(tofree);
}
- ++msg_scrolled;
+ msg_scrolled++;
}
static msgchunk_T *last_msgchunk = NULL; /* last displayed text */
@@ -1968,46 +1921,46 @@ int msg_use_printf(void)
return !embedded_mode && !ui_active();
}
-/*
- * Print a message when there is no valid screen.
- */
-static void msg_puts_printf(char_u *str, int maxlen)
+/// Print a message when there is no valid screen.
+static void msg_puts_printf(char *str, int maxlen)
{
- char_u *s = str;
- char_u buf[4];
- char_u *p;
+ char *s = str;
+ char buf[4];
+ char *p;
while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen)) {
if (!(silent_mode && p_verbose == 0)) {
- /* NL --> CR NL translation (for Unix, not for "--version") */
- /* NL --> CR translation (for Mac) */
+ // NL --> CR NL translation (for Unix, not for "--version")
p = &buf[0];
- if (*s == '\n' && !info_message)
+ if (*s == '\n' && !info_message) {
*p++ = '\r';
+ }
*p++ = *s;
*p = '\0';
- if (info_message) /* informative message, not an error */
- mch_msg((char *)buf);
- else
- mch_errmsg((char *)buf);
+ if (info_message) {
+ mch_msg(buf);
+ } else {
+ mch_errmsg(buf);
+ }
}
- /* primitive way to compute the current column */
+ // primitive way to compute the current column
if (cmdmsg_rl) {
- if (*s == '\r' || *s == '\n')
+ if (*s == '\r' || *s == '\n') {
msg_col = Columns - 1;
- else
- --msg_col;
+ } else {
+ msg_col--;
+ }
} else {
- if (*s == '\r' || *s == '\n')
+ if (*s == '\r' || *s == '\n') {
msg_col = 0;
- else
- ++msg_col;
+ } else {
+ msg_col++;
+ }
}
- ++s;
+ s++;
}
- msg_didout = TRUE; /* assume that line is not empty */
-
+ msg_didout = true; // assume that line is not empty
}
/*
@@ -2019,6 +1972,7 @@ static void msg_puts_printf(char_u *str, int maxlen)
*/
static int do_more_prompt(int typed_char)
{
+ static bool entered = false;
int used_typed_char = typed_char;
int oldState = State;
int c;
@@ -2028,6 +1982,13 @@ static int do_more_prompt(int typed_char)
msgchunk_T *mp;
int i;
+ // We get called recursively when a timer callback outputs a message. In
+ // that case don't show another prompt. Also when at the hit-Enter prompt.
+ if (entered || State == HITRETURN) {
+ return false;
+ }
+ entered = true;
+
if (typed_char == 'G') {
/* "g<": Find first line on the last page. */
mp_last = msg_sb_start(last_msgchunk);
@@ -2202,9 +2163,11 @@ static int do_more_prompt(int typed_char)
if (quit_more) {
msg_row = Rows - 1;
msg_col = 0;
- } else if (cmdmsg_rl)
+ } else if (cmdmsg_rl) {
msg_col = Columns - 1;
+ }
+ entered = false;
return retval;
}
@@ -2572,7 +2535,7 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1)
/* Don't want a hit-enter prompt here. */
++no_wait_return;
- set_vim_var_string(VV_WARNINGMSG, message, -1);
+ set_vim_var_string(VV_WARNINGMSG, (char *) message, -1);
xfree(keep_msg);
keep_msg = NULL;
if (hl)
@@ -3086,7 +3049,7 @@ int vim_snprintf_add(char *str, size_t str_m, char *fmt, ...)
return str_l;
}
-int vim_snprintf(char *str, size_t str_m, char *fmt, ...)
+int vim_snprintf(char *str, size_t str_m, const char *fmt, ...)
{
va_list ap;
int str_l;
@@ -3097,11 +3060,12 @@ int vim_snprintf(char *str, size_t str_m, char *fmt, ...)
return str_l;
}
-int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
+int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
+ typval_T *tvs)
{
size_t str_l = 0;
bool str_avail = str_l < str_m;
- char *p = fmt;
+ const char *p = fmt;
int arg_idx = 1;
if (!p) {
@@ -3135,7 +3099,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
char tmp[TMP_LEN];
// string address in case of string argument
- char *str_arg;
+ const char *str_arg;
// natural field width of arg without padding and sign
size_t str_arg_l;
@@ -3413,8 +3377,8 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
// leave negative numbers for sprintf to handle, to
// avoid handling tricky cases like (short int)-32768
} else if (alternate_form) {
- if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X' ||
- fmt_spec == 'b' || fmt_spec == 'B')) {
+ if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X'
+ || fmt_spec == 'b' || fmt_spec == 'B')) {
tmp[str_arg_l++] = '0';
tmp[str_arg_l++] = fmt_spec;
}
diff --git a/src/nvim/message.h b/src/nvim/message.h
index 019c7bfb73..d3a16fff93 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdarg.h>
#include "nvim/eval_defs.h" // for typval_T
+#include "nvim/ex_cmds_defs.h" // for exarg_T
/*
* Types of dialogs passed to do_dialog().
@@ -24,6 +25,56 @@
#define VIM_ALL 5
#define VIM_DISCARDALL 6
+/// Show plain message
+#define MSG(s) msg((char_u *)(s))
+
+/// Show message highlighted according to the attr
+#define MSG_ATTR(s, attr) msg_attr((char_u *)(s), (attr))
+
+/// Display error message
+///
+/// Sets error flag in process, can be transformed into an exception.
+#define EMSG(s) emsg((char_u *)(s))
+
+/// Like #EMSG, but for messages with one "%s" inside
+#define EMSG2(s, p) emsgf((const char *) (s), (p))
+
+/// Like #EMSG, but for messages with two "%s" inside
+#define EMSG3(s, p, q) emsgf((const char *) (s), (p), (q))
+
+/// Like #EMSG, but for messages with one "%" PRId64 inside
+#define EMSGN(s, n) emsgf((const char *) (s), (int64_t)(n))
+
+/// Like #EMSG, but for messages with one "%" PRIu64 inside
+#define EMSGU(s, n) emsgf((const char *) (s), (uint64_t)(n))
+
+/// Display message at the recorded position
+#define MSG_PUTS(s) msg_puts((char_u *)(s))
+
+/// Display message at the recorded position, highlighted
+#define MSG_PUTS_ATTR(s, a) msg_puts_attr((char_u *)(s), (a))
+
+/// Like #MSG_PUTS, but highlight like title
+#define MSG_PUTS_TITLE(s) msg_puts_title((char_u *)(s))
+
+/// Like #MSG_PUTS, but if middle part of too long messages it will be replaced
+#define MSG_PUTS_LONG(s) msg_puts_long_attr((char_u *)(s), 0)
+
+/// Like #MSG_PUTS_ATTR, but if middle part of long messages will be replaced
+#define MSG_PUTS_LONG_ATTR(s, a) msg_puts_long_attr((char_u *)(s), (a))
+
+/// Message history for `:messages`
+typedef struct msg_hist {
+ struct msg_hist *next; ///< Next message.
+ char_u *msg; ///< Message text.
+ int attr; ///< Message highlighting.
+} MessageHistoryEntry;
+
+/// First message
+extern MessageHistoryEntry *first_msg_hist;
+/// Last message
+extern MessageHistoryEntry *last_msg_hist;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "message.h.generated.h"
#endif
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index 6c969a43fc..48791384a6 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -43,7 +43,6 @@
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
-#include "nvim/tempfile.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/window.h"
@@ -86,38 +85,32 @@ open_line (
int second_line_indent
)
{
- char_u *saved_line; /* copy of the original line */
- char_u *next_line = NULL; /* copy of the next line */
- char_u *p_extra = NULL; /* what goes to next line */
- int less_cols = 0; /* less columns for mark in new line */
- int less_cols_off = 0; /* columns to skip for mark adjust */
- pos_T old_cursor; /* old cursor position */
- int newcol = 0; /* new cursor column */
- int newindent = 0; /* auto-indent of the new line */
- int n;
- int trunc_line = FALSE; /* truncate current line afterwards */
- int retval = FALSE; /* return value, default is FAIL */
- int extra_len = 0; /* length of p_extra string */
- int lead_len; /* length of comment leader */
- char_u *lead_flags; /* position in 'comments' for comment leader */
- char_u *leader = NULL; /* copy of comment leader */
- char_u *allocated = NULL; /* allocated memory */
- char_u *p;
- int saved_char = NUL; /* init for GCC */
- pos_T *pos;
- int do_si = (!p_paste && curbuf->b_p_si
- && !curbuf->b_p_cin
- );
- int no_si = FALSE; /* reset did_si afterwards */
- int first_char = NUL; /* init for GCC */
+ char_u *next_line = NULL; // copy of the next line
+ char_u *p_extra = NULL; // what goes to next line
+ colnr_T less_cols = 0; // less columns for mark in new line
+ colnr_T less_cols_off = 0; // columns to skip for mark adjust
+ pos_T old_cursor; // old cursor position
+ colnr_T newcol = 0; // new cursor column
+ int newindent = 0; // auto-indent of the new line
+ bool trunc_line = false; // truncate current line afterwards
+ bool retval = false; // return value, default is false
+ int extra_len = 0; // length of p_extra string
+ int lead_len; // length of comment leader
+ char_u *lead_flags; // position in 'comments' for comment leader
+ char_u *leader = NULL; // copy of comment leader
+ char_u *allocated = NULL; // allocated memory
+ char_u *p;
+ char_u saved_char = NUL; // init for GCC
+ pos_T *pos;
+ bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin);
+ bool no_si = false; // reset did_si afterwards
+ int first_char = NUL; // init for GCC
int vreplace_mode;
- int did_append; /* appended a new line */
- int saved_pi = curbuf->b_p_pi; /* copy of preserveindent setting */
+ bool did_append; // appended a new line
+ int saved_pi = curbuf->b_p_pi; // copy of preserveindent setting
- /*
- * make a copy of the current line so we can mess with it
- */
- saved_line = vim_strsave(get_cursor_line_ptr());
+ // make a copy of the current line so we can mess with it
+ char_u *saved_line = vim_strsave(get_cursor_line_ptr());
if (State & VREPLACE_FLAG) {
/*
@@ -204,22 +197,19 @@ open_line (
char_u *ptr;
char_u last_char;
- old_cursor = curwin->w_cursor;
+ pos_T old_cursor = curwin->w_cursor;
ptr = saved_line;
if (flags & OPENLINE_DO_COM)
lead_len = get_leader_len(ptr, NULL, FALSE, TRUE);
else
lead_len = 0;
if (dir == FORWARD) {
- /*
- * Skip preprocessor directives, unless they are
- * recognised as comments.
- */
- if (
- lead_len == 0 &&
- ptr[0] == '#') {
- while (ptr[0] == '#' && curwin->w_cursor.lnum > 1)
+ // Skip preprocessor directives, unless they are
+ // recognised as comments.
+ if (lead_len == 0 && ptr[0] == '#') {
+ while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) {
ptr = ml_get(--curwin->w_cursor.lnum);
+ }
newindent = get_indent();
}
if (flags & OPENLINE_DO_COM)
@@ -303,28 +293,26 @@ open_line (
&& cin_is_cinword(ptr))
did_si = TRUE;
}
- } else { /* dir == BACKWARD */
- /*
- * Skip preprocessor directives, unless they are
- * recognised as comments.
- */
- if (
- lead_len == 0 &&
- ptr[0] == '#') {
- int was_backslashed = FALSE;
-
- while ((ptr[0] == '#' || was_backslashed) &&
- curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- if (*ptr && ptr[STRLEN(ptr) - 1] == '\\')
- was_backslashed = TRUE;
- else
- was_backslashed = FALSE;
+ } else { // dir == BACKWARD
+ // Skip preprocessor directives, unless they are
+ // recognised as comments.
+ if (lead_len == 0 && ptr[0] == '#') {
+ bool was_backslashed = false;
+
+ while ((ptr[0] == '#' || was_backslashed)
+ && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
+ if (*ptr && ptr[STRLEN(ptr) - 1] == '\\') {
+ was_backslashed = true;
+ } else {
+ was_backslashed = false;
+ }
ptr = ml_get(++curwin->w_cursor.lnum);
}
- if (was_backslashed)
- newindent = 0; /* Got to end of file */
- else
+ if (was_backslashed) {
+ newindent = 0; // Got to end of file
+ } else {
newindent = get_indent();
+ }
}
p = skipwhite(ptr);
if (*p == '}') /* if line starts with '}': do indent */
@@ -401,7 +389,7 @@ open_line (
end_comment_pending = -1; /* means we want to set it */
++p;
}
- n = copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
+ size_t n = copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
if (end_comment_pending == -1) /* we can set it now */
end_comment_pending = lead_end[n - 1];
@@ -498,10 +486,11 @@ open_line (
}
}
if (lead_len > 0) {
- /* allocate buffer (may concatenate p_extra later) */
- leader = xmalloc(lead_len + lead_repl_len + extra_space + extra_len
- + (second_line_indent > 0 ? second_line_indent : 0) + 1);
- allocated = leader; /* remember to free it later */
+ // allocate buffer (may concatenate p_extra later)
+ leader = xmalloc((size_t)(lead_len + lead_repl_len + extra_space
+ + extra_len + (second_line_indent > 0
+ ? second_line_indent : 0) + 1));
+ allocated = leader; // remember to free it later
STRLCPY(leader, saved_line, lead_len + 1);
@@ -598,9 +587,8 @@ open_line (
if (!ascii_iswhite(*p)) {
/* Don't put a space before a TAB. */
if (p + 1 < leader + lead_len && p[1] == TAB) {
- --lead_len;
- memmove(p, p + 1,
- (leader + lead_len) - p);
+ lead_len--;
+ memmove(p, p + 1, (size_t)(leader + lead_len - p));
} else {
int l = (*mb_ptr2len)(p);
@@ -611,8 +599,7 @@ open_line (
--l;
*p++ = ' ';
}
- memmove(p + 1, p + l,
- (leader + lead_len) - p);
+ memmove(p + 1, p + l, (size_t)(leader + lead_len - p));
lead_len -= l - 1;
}
*p = ' ';
@@ -675,16 +662,12 @@ open_line (
did_si = can_si = FALSE;
} else if (comment_end != NULL) {
- /*
- * We have finished a comment, so we don't use the leader.
- * If this was a C-comment and 'ai' or 'si' is set do a normal
- * indent to align with the line containing the start of the
- * comment.
- */
- if (comment_end[0] == '*' && comment_end[1] == '/' &&
- (curbuf->b_p_ai
- || do_si
- )) {
+ // We have finished a comment, so we don't use the leader.
+ // If this was a C-comment and 'ai' or 'si' is set do a normal
+ // indent to align with the line containing the start of the
+ // comment.
+ if (comment_end[0] == '*' && comment_end[1] == '/'
+ && (curbuf->b_p_ai || do_si)) {
old_cursor = curwin->w_cursor;
curwin->w_cursor.col = (colnr_T)(comment_end - saved_line);
if ((pos = findmatch(NULL, NUL)) != NULL) {
@@ -813,9 +796,11 @@ open_line (
* In REPLACE mode, for each character in the new indent, there must
* be a NUL on the replace stack, for when it is deleted with BS
*/
- if (REPLACE_NORMAL(State))
- for (n = 0; n < (int)curwin->w_cursor.col; ++n)
+ if (REPLACE_NORMAL(State)) {
+ for (colnr_T n = 0; n < curwin->w_cursor.col; n++) {
replace_push(NUL);
+ }
+ }
newcol += curwin->w_cursor.col;
if (no_si)
did_si = FALSE;
@@ -1281,11 +1266,13 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
* Add column offset for 'number', 'relativenumber' and 'foldcolumn'.
*/
width = wp->w_width - win_col_off(wp);
- if (width <= 0)
- return 32000;
- if (col <= (unsigned int)width)
+ if (width <= 0) {
+ return 32000; // bigger than the number of lines of the screen
+ }
+ if (col <= (unsigned int)width) {
return 1;
- col -= width;
+ }
+ col -= (unsigned int)width;
width += win_col_off2(wp);
assert(col <= INT_MAX && (int)col < INT_MAX - (width -1));
return ((int)col + (width - 1)) / width + 1;
@@ -1297,15 +1284,9 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
*/
int plines_win_col(win_T *wp, linenr_T lnum, long column)
{
- long col;
- char_u *s;
- int lines = 0;
- int width;
- char_u *line;
-
- /* Check for filler lines above this buffer line. When folded the result
- * is one line anyway. */
- lines = diff_check_fill(wp, lnum);
+ // Check for filler lines above this buffer line. When folded the result
+ // is one line anyway.
+ int lines = diff_check_fill(wp, lnum);
if (!wp->w_p_wrap)
return lines + 1;
@@ -1313,30 +1294,29 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
if (wp->w_width == 0)
return lines + 1;
- line = s = ml_get_buf(wp->w_buffer, lnum, FALSE);
+ char_u *line = ml_get_buf(wp->w_buffer, lnum, false);
+ char_u *s = line;
- col = 0;
+ colnr_T col = 0;
while (*s != NUL && --column >= 0) {
- col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL);
+ col += win_lbr_chartabsize(wp, line, s, col, NULL);
mb_ptr_adv(s);
}
- /*
- * If *s is a TAB, and the TAB is not displayed as ^I, and we're not in
- * INSERT mode, then col must be adjusted so that it represents the last
- * screen position of the TAB. This only fixes an error when the TAB wraps
- * from one screen line to the next (when 'columns' is not a multiple of
- * 'ts') -- webb.
- */
- if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1))
- col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL) - 1;
+ // If *s is a TAB, and the TAB is not displayed as ^I, and we're not in
+ // INSERT mode, then col must be adjusted so that it represents the last
+ // screen position of the TAB. This only fixes an error when the TAB wraps
+ // from one screen line to the next (when 'columns' is not a multiple of
+ // 'ts') -- webb.
+ if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1)) {
+ col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1;
+ }
- /*
- * Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
- */
- width = wp->w_width - win_col_off(wp);
- if (width <= 0)
+ // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
+ int width = wp->w_width - win_col_off(wp);
+ if (width <= 0) {
return 9999;
+ }
lines += 1;
if (col > width)
@@ -1349,11 +1329,9 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last)
int count = 0;
while (first <= last) {
- int x;
-
- /* Check if there are any really folded lines, but also included lines
- * that are maybe folded. */
- x = foldedCount(wp, first, NULL);
+ // Check if there are any really folded lines, but also included lines
+ // that are maybe folded.
+ linenr_T x = foldedCount(wp, first, NULL);
if (x > 0) {
++count; /* count 1 for "+-- folded" line */
first += x;
@@ -1374,117 +1352,99 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last)
*/
void ins_bytes(char_u *p)
{
- ins_bytes_len(p, (int)STRLEN(p));
+ ins_bytes_len(p, STRLEN(p));
}
-/*
- * Insert string "p" with length "len" at the cursor position.
- * Handles Replace mode and multi-byte characters.
- */
-void ins_bytes_len(char_u *p, int len)
+/// Insert string "p" with length "len" at the cursor position.
+/// Handles Replace mode and multi-byte characters.
+void ins_bytes_len(char_u *p, size_t len)
{
- int i;
- int n;
-
- if (has_mbyte)
- for (i = 0; i < len; i += n) {
- if (enc_utf8)
- /* avoid reading past p[len] */
- n = utfc_ptr2len_len(p + i, len - i);
- else
- n = (*mb_ptr2len)(p + i);
+ if (has_mbyte) {
+ size_t n;
+ for (size_t i = 0; i < len; i += n) {
+ if (enc_utf8) {
+ // avoid reading past p[len]
+ n = (size_t)utfc_ptr2len_len(p + i, (int)(len - i));
+ } else {
+ n = (size_t)(*mb_ptr2len)(p + i);
+ }
ins_char_bytes(p + i, n);
}
- else
- for (i = 0; i < len; ++i)
+ } else {
+ for (size_t i = 0; i < len; i++) {
ins_char(p[i]);
+ }
+ }
}
-/*
- * Insert or replace a single character at the cursor position.
- * When in REPLACE or VREPLACE mode, replace any existing character.
- * Caller must have prepared for undo.
- * For multi-byte characters we get the whole character, the caller must
- * convert bytes to a character.
- */
+/// Insert or replace a single character at the cursor position.
+/// When in REPLACE or VREPLACE mode, replace any existing character.
+/// Caller must have prepared for undo.
+/// For multi-byte characters we get the whole character, the caller must
+/// convert bytes to a character.
void ins_char(int c)
{
char_u buf[MB_MAXBYTES + 1];
- int n;
-
- n = (*mb_char2bytes)(c, buf);
+ size_t n = (size_t)(*mb_char2bytes)(c, buf);
- /* When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte.
- * Happens for CTRL-Vu9900. */
- if (buf[0] == 0)
+ // When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte.
+ // Happens for CTRL-Vu9900.
+ if (buf[0] == 0) {
buf[0] = '\n';
-
+ }
ins_char_bytes(buf, n);
}
-void ins_char_bytes(char_u *buf, int charlen)
+void ins_char_bytes(char_u *buf, size_t charlen)
{
- int c = buf[0];
- int newlen; /* nr of bytes inserted */
- int oldlen; /* nr of bytes deleted (0 when not replacing) */
- char_u *p;
- char_u *newp;
- char_u *oldp;
- int linelen; /* length of old line including NUL */
- colnr_T col;
- linenr_T lnum = curwin->w_cursor.lnum;
- int i;
-
- /* Break tabs if needed. */
- if (virtual_active() && curwin->w_cursor.coladd > 0)
+ // Break tabs if needed.
+ if (virtual_active() && curwin->w_cursor.coladd > 0) {
coladvance_force(getviscol());
+ }
- col = curwin->w_cursor.col;
- oldp = ml_get(lnum);
- linelen = (int)STRLEN(oldp) + 1;
+ int c = buf[0];
+ size_t col = (size_t)curwin->w_cursor.col;
+ linenr_T lnum = curwin->w_cursor.lnum;
+ char_u *oldp = ml_get(lnum);
+ size_t linelen = STRLEN(oldp) + 1; // length of old line including NUL
- /* The lengths default to the values for when not replacing. */
- oldlen = 0;
- newlen = charlen;
+ // The lengths default to the values for when not replacing.
+ size_t oldlen = 0; // nr of bytes inserted
+ size_t newlen = charlen; // nr of bytes deleted (0 when not replacing)
if (State & REPLACE_FLAG) {
if (State & VREPLACE_FLAG) {
- colnr_T new_vcol = 0; /* init for GCC */
+ // Disable 'list' temporarily, unless 'cpo' contains the 'L' flag.
+ // Returns the old value of list, so when finished,
+ // curwin->w_p_list should be set back to this.
+ int old_list = curwin->w_p_list;
+ if (old_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) {
+ curwin->w_p_list = false;
+ }
+ // In virtual replace mode each character may replace one or more
+ // characters (zero if it's a TAB). Count the number of bytes to
+ // be deleted to make room for the new character, counting screen
+ // cells. May result in adding spaces to fill a gap.
colnr_T vcol;
- int old_list;
-
- /*
- * Disable 'list' temporarily, unless 'cpo' contains the 'L' flag.
- * Returns the old value of list, so when finished,
- * curwin->w_p_list should be set back to this.
- */
- old_list = curwin->w_p_list;
- if (old_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL)
- curwin->w_p_list = FALSE;
-
- /*
- * In virtual replace mode each character may replace one or more
- * characters (zero if it's a TAB). Count the number of bytes to
- * be deleted to make room for the new character, counting screen
- * cells. May result in adding spaces to fill a gap.
- */
getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL);
- new_vcol = vcol + chartabsize(buf, vcol);
+ colnr_T new_vcol = vcol + chartabsize(buf, vcol);
while (oldp[col + oldlen] != NUL && vcol < new_vcol) {
vcol += chartabsize(oldp + col + oldlen, vcol);
- /* Don't need to remove a TAB that takes us to the right
- * position. */
- if (vcol > new_vcol && oldp[col + oldlen] == TAB)
+ // Don't need to remove a TAB that takes us to the right
+ // position.
+ if (vcol > new_vcol && oldp[col + oldlen] == TAB) {
break;
- oldlen += (*mb_ptr2len)(oldp + col + oldlen);
- /* Deleted a bit too much, insert spaces. */
- if (vcol > new_vcol)
- newlen += vcol - new_vcol;
+ }
+ oldlen += (size_t)(*mb_ptr2len)(oldp + col + oldlen);
+ // Deleted a bit too much, insert spaces.
+ if (vcol > new_vcol) {
+ newlen += (size_t)(vcol - new_vcol);
+ }
}
curwin->w_p_list = old_list;
} else if (oldp[col] != NUL) {
- /* normal replace */
- oldlen = (*mb_ptr2len)(oldp + col);
+ // normal replace
+ oldlen = (size_t)(*mb_ptr2len)(oldp + col);
}
@@ -1493,38 +1453,39 @@ void ins_char_bytes(char_u *buf, int charlen)
* done the other way around, so that the first byte is popped off
* first (it tells the byte length of the character). */
replace_push(NUL);
- for (i = 0; i < oldlen; ++i) {
- if (has_mbyte)
- i += replace_push_mb(oldp + col + i) - 1;
- else
+ for (size_t i = 0; i < oldlen; i++) {
+ if (has_mbyte) {
+ i += (size_t)replace_push_mb(oldp + col + i) - 1;
+ } else {
replace_push(oldp[col + i]);
+ }
}
}
- newp = (char_u *) xmalloc((size_t)(linelen + newlen - oldlen));
+ char_u *newp = (char_u *) xmalloc((size_t)(linelen + newlen - oldlen));
- /* Copy bytes before the cursor. */
- if (col > 0)
+ // Copy bytes before the cursor.
+ if (col > 0) {
memmove(newp, oldp, (size_t)col);
+ }
- /* Copy bytes after the changed character(s). */
- p = newp + col;
- memmove(p + newlen, oldp + col + oldlen,
- (size_t)(linelen - col - oldlen));
+ // Copy bytes after the changed character(s).
+ char_u *p = newp + col;
+ memmove(p + newlen, oldp + col + oldlen, (size_t)(linelen - col - oldlen));
- /* Insert or overwrite the new character. */
+ // Insert or overwrite the new character.
memmove(p, buf, charlen);
- i = charlen;
- /* Fill with spaces when necessary. */
- while (i < newlen)
- p[i++] = ' ';
+ // Fill with spaces when necessary.
+ for (size_t i = charlen; i < newlen; i++) {
+ p[i] = ' ';
+ }
/* Replace the line in the buffer. */
ml_replace(lnum, newp, FALSE);
- /* mark the buffer as changed and prepare for displaying */
- changed_bytes(lnum, col);
+ // mark the buffer as changed and prepare for displaying
+ changed_bytes(lnum, (colnr_T)col);
/*
* If we're in Insert or Replace mode and 'showmatch' is set, then briefly
@@ -1541,8 +1502,8 @@ void ins_char_bytes(char_u *buf, int charlen)
}
if (!p_ri || (State & REPLACE_FLAG)) {
- /* Normal insert: move cursor right */
- curwin->w_cursor.col += charlen;
+ // Normal insert: move cursor right
+ curwin->w_cursor.col += (int)charlen;
}
/*
* TODO: should try to update w_row here, to avoid recomputing it later.
@@ -1595,7 +1556,7 @@ int del_char(int fixpos)
return FAIL;
return del_chars(1L, fixpos);
}
- return del_bytes(1L, fixpos, TRUE);
+ return del_bytes(1, fixpos, true);
}
/*
@@ -1603,7 +1564,7 @@ int del_char(int fixpos)
*/
int del_chars(long count, int fixpos)
{
- long bytes = 0;
+ int bytes = 0;
long i;
char_u *p;
int l;
@@ -1617,30 +1578,22 @@ int del_chars(long count, int fixpos)
return del_bytes(bytes, fixpos, TRUE);
}
-/*
- * Delete "count" bytes under the cursor.
- * If "fixpos" is TRUE, don't leave the cursor on the NUL after the line.
- * Caller must have prepared for undo.
- *
- * return FAIL for failure, OK otherwise
- */
-int
-del_bytes (
- long count,
- int fixpos_arg,
- int use_delcombine /* 'delcombine' option applies */
-)
+/// Delete "count" bytes under the cursor.
+/// If "fixpos" is true, don't leave the cursor on the NUL after the line.
+/// Caller must have prepared for undo.
+///
+/// @param count number of bytes to be deleted
+/// @param fixpos_arg leave the cursor on the NUL after the line
+/// @param use_delcombine 'delcombine' option applies
+///
+/// @return FAIL for failure, OK otherwise
+int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
{
- char_u *oldp, *newp;
- colnr_T oldlen;
linenr_T lnum = curwin->w_cursor.lnum;
colnr_T col = curwin->w_cursor.col;
- int was_alloced;
- long movelen;
- int fixpos = fixpos_arg;
-
- oldp = ml_get(lnum);
- oldlen = (int)STRLEN(oldp);
+ bool fixpos = fixpos_arg;
+ char_u *oldp = ml_get(lnum);
+ colnr_T oldlen = (colnr_T)STRLEN(oldp);
/*
* Can't do anything when the cursor is on the NUL after the line.
@@ -1664,14 +1617,12 @@ del_bytes (
count = utf_ptr2len(oldp + n);
n += count;
} while (UTF_COMPOSINGLIKE(oldp + col, oldp + n));
- fixpos = 0;
+ fixpos = false;
}
}
- /*
- * When count is too big, reduce it.
- */
- movelen = (long)oldlen - (long)col - count + 1; /* includes trailing NUL */
+ // When count is too big, reduce it.
+ int movelen = oldlen - col - count + 1; // includes trailing NUL
if (movelen <= 1) {
/*
* If we just took off the last character of a non-blank line, and
@@ -1691,15 +1642,14 @@ del_bytes (
movelen = 1;
}
- /*
- * If the old line has been allocated the deletion can be done in the
- * existing line. Otherwise a new line has to be allocated.
- */
- was_alloced = ml_line_alloced(); /* check if oldp was allocated */
- if (was_alloced)
- newp = oldp; /* use same allocated memory */
- else { /* need to allocate a new line */
- newp = xmalloc(oldlen + 1 - count);
+ // If the old line has been allocated the deletion can be done in the
+ // existing line. Otherwise a new line has to be allocated.
+ bool was_alloced = ml_line_alloced(); // check if oldp was allocated
+ char_u *newp;
+ if (was_alloced) {
+ newp = oldp; // use same allocated memory
+ } else { // need to allocate a new line
+ newp = xmalloc((size_t)(oldlen + 1 - count));
memmove(newp, oldp, (size_t)col);
}
memmove(newp + col, oldp + col + count, (size_t)movelen);
@@ -1725,12 +1675,12 @@ truncate_line (
linenr_T lnum = curwin->w_cursor.lnum;
colnr_T col = curwin->w_cursor.col;
- if (col == 0)
+ if (col == 0) {
newp = vim_strsave((char_u *)"");
- else
- newp = vim_strnsave(ml_get(lnum), col);
-
- ml_replace(lnum, newp, FALSE);
+ } else {
+ newp = vim_strnsave(ml_get(lnum), (size_t)col);
+ }
+ ml_replace(lnum, newp, false);
/* mark the buffer as changed and prepare for displaying */
changed_bytes(lnum, curwin->w_cursor.col);
@@ -1827,6 +1777,9 @@ void changed(void)
if (curbuf->b_may_swap
&& !bt_dontwrite(curbuf)
) {
+ int save_need_wait_return = need_wait_return;
+
+ need_wait_return = false;
ml_open_file(curbuf);
/* The ml_open_file() can cause an ATTENTION message.
@@ -1838,6 +1791,8 @@ void changed(void)
os_delay(2000L, true);
wait_return(TRUE);
msg_scroll = save_msg_scroll;
+ } else {
+ need_wait_return = save_need_wait_return;
}
}
changed_int();
@@ -1985,13 +1940,13 @@ changed_lines (
changed_common(lnum, col, lnume, xtra);
}
-static void
-changed_lines_buf (
- buf_T *buf,
- linenr_T lnum, /* first line with change */
- linenr_T lnume, /* line below last changed line */
- long xtra /* number of extra lines (negative when deleting) */
-)
+/// Mark line range in buffer as changed.
+///
+/// @param buf the buffer where lines were changed
+/// @param lnum first line with change
+/// @param lnume line below last changed line
+/// @param xtra number of extra lines (negative when deleting)
+void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, long xtra)
{
if (buf->b_mod_set) {
/* find the maximum area that must be redisplayed */
@@ -2250,7 +2205,7 @@ change_warning (
msg_col = col;
msg_source(hl_attr(HLF_W));
MSG_PUTS_ATTR(_(w_readonly), hl_attr(HLF_W) | MSG_HIST);
- set_vim_var_string(VV_WARNINGMSG, (char_u *)_(w_readonly), -1);
+ set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1);
msg_clr_eos();
(void)msg_end();
if (msg_silent == 0 && !silent_mode) {
@@ -2360,13 +2315,13 @@ int get_keystroke(void)
* 5 chars plus NUL). And fix_input_buffer() can triple the number of
* bytes. */
maxlen = (buflen - 6 - len) / 3;
- if (buf == NULL)
- buf = xmalloc(buflen);
- else if (maxlen < 10) {
- /* Need some more space. This might happen when receiving a long
- * escape sequence. */
+ 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, buflen);
+ buf = xrealloc(buf, (size_t)buflen);
maxlen = (buflen - 6 - len) / 3;
}
@@ -2729,38 +2684,33 @@ void fast_breakcheck(void)
}
}
-/*
- * 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.
- * Returns an allocated string, or NULL for error.
- */
-char_u *
-get_cmd_output (
- char_u *cmd,
- char_u *infile, /* optional input file name */
- int flags, // can be kShellOptSilent
- size_t *ret_len
-)
+/// 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 *tempname;
- char_u *command;
- char_u *buffer = NULL;
- int len;
- int i = 0;
- FILE *fd;
+ char_u *buffer = NULL;
if (check_restricted() || check_secure())
return NULL;
- /* get a name for the temp file */
- if ((tempname = vim_tempname()) == 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 */
- command = make_filter_cmd(cmd, infile, tempname);
+ // Add the redirection stuff
+ char_u *command = make_filter_cmd(cmd, infile, tempname);
/*
* Call the shell to execute the command (errors are ignored).
@@ -2772,10 +2722,8 @@ get_cmd_output (
xfree(command);
- /*
- * read the names from the file into memory
- */
- fd = mch_fopen((char *)tempname, READBIN);
+ // read the names from the file into memory
+ FILE *fd = mch_fopen((char *)tempname, READBIN);
if (fd == NULL) {
EMSG2(_(e_notopen), tempname);
@@ -2783,11 +2731,11 @@ get_cmd_output (
}
fseek(fd, 0L, SEEK_END);
- len = ftell(fd); /* get size of temp file */
+ size_t len = (size_t)ftell(fd); // get size of temp file
fseek(fd, 0L, SEEK_SET);
buffer = xmalloc(len + 1);
- i = (int)fread((char *)buffer, (size_t)1, (size_t)len, fd);
+ size_t i = fread((char *)buffer, 1, len, fd);
fclose(fd);
os_remove((char *)tempname);
if (i != len) {
diff --git a/src/nvim/misc1.h b/src/nvim/misc1.h
index 11891d6f7e..f0f66854d8 100644
--- a/src/nvim/misc1.h
+++ b/src/nvim/misc1.h
@@ -2,6 +2,7 @@
#define NVIM_MISC1_H
#include "nvim/vim.h"
+#include "nvim/os/shell.h"
/* flags for open_line() */
#define OPENLINE_DELSPACES 1 /* delete spaces after cursor */
diff --git a/src/nvim/misc2.c b/src/nvim/misc2.c
index 3c0a1414a6..368f83cfb5 100644
--- a/src/nvim/misc2.c
+++ b/src/nvim/misc2.c
@@ -327,9 +327,10 @@ int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
}
}
- set_vim_var_nr(VV_SHELL_ERROR, (long)retval);
- if (do_profiling == PROF_YES)
+ set_vim_var_nr(VV_SHELL_ERROR, (varnumber_T) retval);
+ if (do_profiling == PROF_YES) {
prof_child_exit(&wait_time);
+ }
return retval;
}
@@ -466,11 +467,12 @@ bool put_bytes(FILE *fd, uintmax_t number, size_t len)
}
/// Writes time_t to file "fd" in 8 bytes.
-void put_time(FILE *fd, time_t time_)
+/// @returns FAIL when the write failed.
+int put_time(FILE *fd, time_t time_)
{
uint8_t buf[8];
time_to_bytes(time_, buf);
- (void)fwrite(buf, sizeof(uint8_t), ARRAY_SIZE(buf), fd);
+ return fwrite(buf, sizeof(uint8_t), ARRAY_SIZE(buf), fd) == 1 ? OK : FAIL;
}
/// Writes time_t to "buf[8]".
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 3ae1a6a890..2f499e477c 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -14,6 +14,8 @@
#include "nvim/misc1.h"
#include "nvim/cursor.h"
#include "nvim/buffer_defs.h"
+#include "nvim/memline.h"
+#include "nvim/charset.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mouse.c.generated.h"
@@ -503,3 +505,95 @@ void set_mouse_topline(win_T *wp)
orig_topfill = wp->w_topfill;
}
+///
+/// Return length of line "lnum" for horizontal scrolling.
+///
+static colnr_T scroll_line_len(linenr_T lnum)
+{
+ colnr_T col = 0;
+ char_u *line = ml_get(lnum);
+ if (*line != NUL) {
+ for (;;) {
+ int numchar = chartabsize(line, col);
+ mb_ptr_adv(line);
+ if (*line == NUL) { // don't count the last character
+ break;
+ }
+ col += numchar;
+ }
+ }
+ return col;
+}
+
+///
+/// Find longest visible line number.
+///
+static linenr_T find_longest_lnum(void)
+{
+ linenr_T ret = 0;
+
+ // Calculate maximum for horizontal scrollbar. Check for reasonable
+ // line numbers, topline and botline can be invalid when displaying is
+ // postponed.
+ if (curwin->w_topline <= curwin->w_cursor.lnum
+ && curwin->w_botline > curwin->w_cursor.lnum
+ && curwin->w_botline <= curbuf->b_ml.ml_line_count + 1) {
+ long max = 0;
+
+ // Use maximum of all visible lines. Remember the lnum of the
+ // longest line, closest to the cursor line. Used when scrolling
+ // below.
+ for (linenr_T lnum = curwin->w_topline; lnum < curwin->w_botline; lnum++) {
+ colnr_T len = scroll_line_len(lnum);
+ if (len > (colnr_T)max) {
+ max = len;
+ ret = lnum;
+ } else if (len == (colnr_T)max
+ && abs((int)(lnum - curwin->w_cursor.lnum))
+ < abs((int)(ret - curwin->w_cursor.lnum))) {
+ ret = lnum;
+ }
+ }
+ } else {
+ // Use cursor line only.
+ ret = curwin->w_cursor.lnum;
+ }
+
+ return ret;
+}
+
+///
+/// Do a horizontal scroll. Return TRUE if the cursor moved, FALSE otherwise.
+///
+bool mouse_scroll_horiz(int dir)
+{
+ if (curwin->w_p_wrap) {
+ return false;
+ }
+
+ int step = 6;
+ if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
+ step = curwin->w_width;
+ }
+
+ int leftcol = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : +step);
+ if (leftcol < 0) {
+ leftcol = 0;
+ }
+
+ if (curwin->w_leftcol == leftcol) {
+ return false;
+ }
+
+ curwin->w_leftcol = (colnr_T)leftcol;
+
+ // When the line of the cursor is too short, move the cursor to the
+ // longest visible line.
+ if (!virtual_active()
+ && (colnr_T)leftcol > scroll_line_len(curwin->w_cursor.lnum)) {
+ curwin->w_cursor.lnum = find_longest_lnum();
+ curwin->w_cursor.col = 0;
+ }
+
+ return leftcol_changed();
+}
diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h
index c824bcc8f0..0149f7c7c0 100644
--- a/src/nvim/mouse.h
+++ b/src/nvim/mouse.h
@@ -34,6 +34,12 @@
#define MOUSE_X1 0x300 // Mouse-button X1 (6th)
#define MOUSE_X2 0x400 // Mouse-button X2
+// Direction for nv_mousescroll() and ins_mousescroll()
+#define MSCR_DOWN 0 // DOWN must be FALSE
+#define MSCR_UP 1
+#define MSCR_LEFT -1
+#define MSCR_RIGHT -2
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mouse.h.generated.h"
diff --git a/src/nvim/move.c b/src/nvim/move.c
index eb55397511..b129c5cb7a 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -1010,12 +1010,9 @@ scrollup (
int byfold /* true: count a closed fold as one line */
)
{
- if (
- (byfold && hasAnyFolding(curwin))
- ||
- curwin->w_p_diff
- ) {
- /* count each sequence of folded lines as one logical line */
+ if ((byfold && hasAnyFolding(curwin))
+ || curwin->w_p_diff) {
+ // count each sequence of folded lines as one logical line
linenr_T lnum = curwin->w_topline;
while (line_count--) {
if (curwin->w_topfill > 0)
@@ -1288,9 +1285,10 @@ void scroll_cursor_top(int min_scroll, int always)
* - at least 'scrolloff' lines above and below the cursor
*/
validate_cheight();
- int used = curwin->w_cline_height;
- if (curwin->w_cursor.lnum < curwin->w_topline)
+ int used = curwin->w_cline_height; // includes filler lines above
+ if (curwin->w_cursor.lnum < curwin->w_topline) {
scrolled = used;
+ }
if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) {
--top;
@@ -1301,9 +1299,10 @@ void scroll_cursor_top(int min_scroll, int always)
}
new_topline = top + 1;
- /* count filler lines of the cursor window as context */
+ // "used" already contains the number of filler lines above, don't add it
+ // again.
+ // Hide filler lines above cursor line by adding them to "extra".
int extra = diff_check_fill(curwin, curwin->w_cursor.lnum);
- used += extra;
/*
* Check if the lines from "top" to "bot" fit in the window. If they do,
@@ -1312,7 +1311,7 @@ void scroll_cursor_top(int min_scroll, int always)
while (top > 0) {
int i = hasFolding(top, &top, NULL)
? 1 // count one logical line for a sequence of folded lines
- : plines(top);
+ : plines_nofill(top);
used += i;
if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) {
if (hasFolding(bot, NULL, &bot))
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 34ff7c6374..3a6d7c1434 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -7,8 +7,8 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
+#include "nvim/api/ui.h"
#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/msgpack_rpc/remote_ui.h"
#include "nvim/event/loop.h"
#include "nvim/event/libuv_process.h"
#include "nvim/event/rstream.h"
diff --git a/src/nvim/msgpack_rpc/defs.h b/src/nvim/msgpack_rpc/defs.h
index d97cf28ca1..5611636d4f 100644
--- a/src/nvim/msgpack_rpc/defs.h
+++ b/src/nvim/msgpack_rpc/defs.h
@@ -1,8 +1,6 @@
#ifndef NVIM_MSGPACK_RPC_DEFS_H
#define NVIM_MSGPACK_RPC_DEFS_H
-#include <msgpack.h>
-
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
/// functions of this type.
@@ -24,22 +22,6 @@ void msgpack_rpc_add_method_handler(String method,
void msgpack_rpc_init_function_metadata(Dictionary *metadata);
-/// Dispatches to the actual API function after basic payload validation by
-/// `msgpack_rpc_call`. It is responsible for validating/converting arguments
-/// to C types, and converting the return value back to msgpack types.
-/// The implementation is generated at compile time with metadata extracted
-/// from the api/*.h headers,
-///
-/// @param channel_id The channel id
-/// @param method_id The method id
-/// @param req The parsed request object
-/// @param error Pointer to error structure
-/// @return Some object
-Object msgpack_rpc_dispatch(uint64_t channel_id,
- msgpack_object *req,
- Error *error)
- FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3);
-
MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name,
size_t name_len)
FUNC_ATTR_NONNULL_ARG(1);
diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c
index 5ef81721d4..0049ae6b95 100644
--- a/src/nvim/msgpack_rpc/helpers.c
+++ b/src/nvim/msgpack_rpc/helpers.c
@@ -419,8 +419,8 @@ void msgpack_rpc_validate(uint64_t *response_id,
return;
}
- if ((type == kMessageTypeRequest && req->via.array.size != 4) ||
- (type == kMessageTypeNotification && req->via.array.size != 3)) {
+ if ((type == kMessageTypeRequest && req->via.array.size != 4)
+ || (type == kMessageTypeNotification && req->via.array.size != 3)) {
api_set_error(err, Validation, _("Request array size should be 4 (request) "
"or 3 (notification)"));
return;
diff --git a/src/nvim/msgpack_rpc/remote_ui.h b/src/nvim/msgpack_rpc/remote_ui.h
deleted file mode 100644
index 8af86dc1b8..0000000000
--- a/src/nvim/msgpack_rpc/remote_ui.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef NVIM_MSGPACK_RPC_REMOTE_UI_H
-#define NVIM_MSGPACK_RPC_REMOTE_UI_H
-
-#include "nvim/ui.h"
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "msgpack_rpc/remote_ui.h.generated.h"
-#endif
-#endif // NVIM_MSGPACK_RPC_REMOTE_UI_H
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index 474e25ffeb..6cc56ba3dd 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -14,7 +14,7 @@
#include "nvim/vim.h"
#include "nvim/memory.h"
#include "nvim/log.h"
-#include "nvim/tempfile.h"
+#include "nvim/fileio.h"
#include "nvim/path.h"
#include "nvim/strings.h"
@@ -59,7 +59,7 @@ static void set_vservername(garray_T *srvs)
char *default_server = (srvs->ga_len > 0)
? ((SocketWatcher **)srvs->ga_data)[0]->addr
: NULL;
- set_vim_var_string(VV_SEND_SERVER, (char_u *)default_server, -1);
+ set_vim_var_string(VV_SEND_SERVER, default_server, -1);
}
/// Teardown the server module
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 9a9cf50e48..382c4943ff 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -569,36 +569,33 @@ static bool normal_need_aditional_char(NormalState *s)
static bool normal_need_redraw_mode_message(NormalState *s)
{
return (
- (
// 'showmode' is set and messages can be printed
- p_smd && msg_silent == 0
- // must restart insert mode(ctrl+o or ctrl+l) or we just entered visual
- // mode
- && (restart_edit != 0 || (VIsual_active
- && s->old_pos.lnum == curwin->w_cursor.lnum
- && s->old_pos.col == curwin->w_cursor.col))
- // command-line must be cleared or redrawn
- && (clear_cmdline || redraw_cmdline)
- // some message was printed or scrolled
- && (msg_didout || (msg_didany && msg_scroll))
- // it is fine to remove the current message
- && !msg_nowait
- // the command was the result of direct user input and not a mapping
- && KeyTyped
- )
- ||
- // must restart insert mode, not in visual mode and error message is
- // being shown
- (restart_edit != 0 && !VIsual_active && (msg_scroll && emsg_on_display))
- )
- // no register was used
- && s->oa.regname == 0
- && !(s->ca.retval & CA_COMMAND_BUSY)
- && stuff_empty()
- && typebuf_typed()
- && emsg_silent == 0
- && !did_wait_return
- && s->oa.op_type == OP_NOP;
+ ((p_smd && msg_silent == 0
+ // must restart insert mode(ctrl+o or ctrl+l) or we just entered visual
+ // mode
+ && (restart_edit != 0 || (VIsual_active
+ && s->old_pos.lnum == curwin->w_cursor.lnum
+ && s->old_pos.col == curwin->w_cursor.col))
+ // command-line must be cleared or redrawn
+ && (clear_cmdline || redraw_cmdline)
+ // some message was printed or scrolled
+ && (msg_didout || (msg_didany && msg_scroll))
+ // it is fine to remove the current message
+ && !msg_nowait
+ // the command was the result of direct user input and not a mapping
+ && KeyTyped)
+ // must restart insert mode, not in visual mode and error message is
+ // being shown
+ || (restart_edit != 0 && !VIsual_active && msg_scroll
+ && emsg_on_display))
+ // no register was used
+ && s->oa.regname == 0
+ && !(s->ca.retval & CA_COMMAND_BUSY)
+ && stuff_empty()
+ && typebuf_typed()
+ && emsg_silent == 0
+ && !did_wait_return
+ && s->oa.op_type == OP_NOP);
}
static void normal_redraw_mode_message(NormalState *s)
@@ -1436,19 +1433,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
}
curwin->w_p_lbr = false;
oap->is_VIsual = VIsual_active;
- if (oap->motion_force == 'V')
- oap->motion_type = MLINE;
- else if (oap->motion_force == 'v') {
- /* If the motion was linewise, "inclusive" will not have been set.
- * Use "exclusive" to be consistent. Makes "dvj" work nice. */
- if (oap->motion_type == MLINE)
+ if (oap->motion_force == 'V') {
+ oap->motion_type = kMTLineWise;
+ } else if (oap->motion_force == 'v') {
+ // If the motion was linewise, "inclusive" will not have been set.
+ // Use "exclusive" to be consistent. Makes "dvj" work nice.
+ if (oap->motion_type == kMTLineWise) {
oap->inclusive = false;
- /* If the motion already was characterwise, toggle "inclusive" */
- else if (oap->motion_type == MCHAR)
+ } else if (oap->motion_type == kMTCharWise) {
+ // If the motion already was characterwise, toggle "inclusive"
oap->inclusive = !oap->inclusive;
- oap->motion_type = MCHAR;
+ }
+ oap->motion_type = kMTCharWise;
} else if (oap->motion_force == Ctrl_V) {
- /* Change line- or characterwise motion into Visual block mode. */
+ // Change line- or characterwise motion into Visual block mode.
VIsual_active = true;
VIsual = oap->start;
VIsual_mode = Ctrl_V;
@@ -1538,9 +1536,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
curbuf->b_visual_mode_eval = VIsual_mode;
}
- /* In Select mode, a linewise selection is operated upon like a
- * characterwise selection. */
- if (VIsual_select && VIsual_mode == 'V') {
+ // In Select mode, a linewise selection is operated upon like a
+ // characterwise selection.
+ // Special case: gH<Del> deletes the last line.
+ if (VIsual_select && VIsual_mode == 'V'
+ && cap->oap->op_type != OP_DELETE) {
if (lt(VIsual, curwin->w_cursor)) {
VIsual.col = 0;
curwin->w_cursor.col =
@@ -1584,13 +1584,15 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
* automatically. */
curwin->w_valid &= ~VALID_VIRTCOL;
} else {
- /* Include folded lines completely. */
- if (!VIsual_active && oap->motion_type == MLINE) {
+ // Include folded lines completely.
+ if (!VIsual_active && oap->motion_type == kMTLineWise) {
if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum,
- NULL))
+ NULL)) {
curwin->w_cursor.col = 0;
- if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum))
+ }
+ if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) {
oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum));
+ }
}
oap->end = oap->start;
oap->start = curwin->w_cursor;
@@ -1661,35 +1663,29 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
}
}
- /*
- * oap->inclusive defaults to true.
- * If oap->end is on a NUL (empty line) oap->inclusive becomes
- * false. This makes "d}P" and "v}dP" work the same.
- */
- if (oap->motion_force == NUL || oap->motion_type == MLINE)
+ // oap->inclusive defaults to true.
+ // If oap->end is on a NUL (empty line) oap->inclusive becomes
+ // false. This makes "d}P" and "v}dP" work the same.
+ if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) {
oap->inclusive = true;
+ }
if (VIsual_mode == 'V') {
- oap->motion_type = MLINE;
+ oap->motion_type = kMTLineWise;
} else if (VIsual_mode == 'v') {
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
if (*ml_get_pos(&(oap->end)) == NUL
&& (include_line_break || !virtual_op)
) {
oap->inclusive = false;
- /* Try to include the newline, unless it's an operator
- * that works on lines only. */
- if (*p_sel != 'o' && !op_on_lines(oap->op_type)) {
- if (oap->end.lnum < curbuf->b_ml.ml_line_count) {
- ++oap->end.lnum;
- oap->end.col = 0;
- oap->end.coladd = 0;
- ++oap->line_count;
- } else {
- /* Cannot move below the last line, make the op
- * inclusive to tell the operation to include the
- * line break. */
- oap->inclusive = true;
- }
+ // Try to include the newline, unless it's an operator
+ // that works on lines only.
+ if (*p_sel != 'o'
+ && !op_on_lines(oap->op_type)
+ && oap->end.lnum < curbuf->b_ml.ml_line_count) {
+ oap->end.lnum++;
+ oap->end.col = 0;
+ oap->end.coladd = 0;
+ oap->line_count++;
}
}
}
@@ -1734,7 +1730,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
* oap->empty is set when start and end are the same. The inclusive
* flag affects this too, unless yanking and the end is on a NUL.
*/
- oap->empty = (oap->motion_type != MLINE
+ oap->empty = (oap->motion_type != kMTLineWise
&& (!oap->inclusive
|| (oap->op_type == OP_YANK
&& gchar_pos(&oap->end) == NUL))
@@ -1759,23 +1755,23 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
/*
* If the end of an operator is in column one while oap->motion_type
- * is MCHAR and oap->inclusive is false, we put op_end after the last
+ * is kMTCharWise and oap->inclusive is false, we put op_end after the last
* character in the previous line. If op_start is on or before the
* first non-blank in the line, the operator becomes linewise
* (strange, but that's the way vi does it).
*/
- if (oap->motion_type == MCHAR
+ if (oap->motion_type == kMTCharWise
&& oap->inclusive == false
&& !(cap->retval & CA_NO_ADJ_OP_END)
&& oap->end.col == 0
&& (!oap->is_VIsual || *p_sel == 'o')
&& oap->line_count > 1) {
oap->end_adjusted = true; // remember that we did this
- --oap->line_count;
- --oap->end.lnum;
- if (inindent(0))
- oap->motion_type = MLINE;
- else {
+ oap->line_count--;
+ oap->end.lnum--;
+ if (inindent(0)) {
+ oap->motion_type = kMTLineWise;
+ } else {
oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
if (oap->end.col) {
--oap->end.col;
@@ -1814,8 +1810,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
CancelRedo();
} else {
(void)op_delete(oap);
- if (oap->motion_type == MLINE && has_format_option(FO_AUTO))
- u_save_cursor(); /* cursor line wasn't saved yet */
+ if (oap->motion_type == kMTLineWise && has_format_option(FO_AUTO)) {
+ u_save_cursor(); // cursor line wasn't saved yet
+ }
auto_format(false, true);
}
break;
@@ -2014,7 +2011,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
/*
* if 'sol' not set, go back to old column for some commands
*/
- if (!p_sol && oap->motion_type == MLINE && !oap->end_adjusted
+ if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted
&& (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT
|| oap->op_type == OP_DELETE)) {
curwin->w_p_lbr = false;
@@ -2089,13 +2086,14 @@ static void op_function(oparg_T *oap)
/* Set '[ and '] marks to text to be operated on. */
curbuf->b_op_start = oap->start;
curbuf->b_op_end = oap->end;
- if (oap->motion_type != MLINE && !oap->inclusive)
- /* Exclude the end position. */
+ if (oap->motion_type != kMTLineWise && !oap->inclusive) {
+ // Exclude the end position.
decl(&curbuf->b_op_end);
+ }
- if (oap->motion_type == MBLOCK) {
+ if (oap->motion_type == kMTBlockWise) {
argv[0] = (char_u *)"block";
- } else if (oap->motion_type == MLINE) {
+ } else if (oap->motion_type == kMTLineWise) {
argv[0] = (char_u *)"line";
} else {
argv[0] = (char_u *)"char";
@@ -2347,9 +2345,12 @@ do_mouse (
if (mouse_row == 0 && firstwin->w_winrow > 0) {
if (is_drag) {
if (in_tab_line) {
- tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose
- ? 9999
- : tab_page_click_defs[mouse_col].tabnr - 1);
+ if (tab_page_click_defs[mouse_col].type == kStlClickTabClose) {
+ tabpage_move(9999);
+ } else {
+ int tabnr = tab_page_click_defs[mouse_col].tabnr;
+ tabpage_move(tabnr < tabpage_index(curtab) ? tabnr - 1 : tabnr);
+ }
}
return false;
}
@@ -2530,7 +2531,7 @@ do_mouse (
*/
if (!is_drag && oap != NULL && oap->op_type != OP_NOP) {
got_click = false;
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
}
/* When releasing the button let jump_to_mouse() know. */
@@ -2598,11 +2599,10 @@ do_mouse (
end_visual.col = leftcol;
else
end_visual.col = rightcol;
- if (curwin->w_cursor.lnum <
- (start_visual.lnum + end_visual.lnum) / 2)
- end_visual.lnum = end_visual.lnum;
- else
+ if (curwin->w_cursor.lnum >=
+ (start_visual.lnum + end_visual.lnum) / 2) {
end_visual.lnum = start_visual.lnum;
+ }
/* move VIsual to the right column */
start_visual = curwin->w_cursor; /* save the cursor pos */
@@ -2770,21 +2770,23 @@ do_mouse (
end_visual = curwin->w_cursor;
while (gc = gchar_pos(&end_visual), ascii_iswhite(gc))
inc(&end_visual);
- if (oap != NULL)
- oap->motion_type = MCHAR;
+ if (oap != NULL) {
+ oap->motion_type = kMTCharWise;
+ }
if (oap != NULL
&& VIsual_mode == 'v'
&& !vim_iswordc(gchar_pos(&end_visual))
&& equalpos(curwin->w_cursor, VIsual)
&& (pos = findmatch(oap, NUL)) != NULL) {
curwin->w_cursor = *pos;
- if (oap->motion_type == MLINE)
+ if (oap->motion_type == kMTLineWise) {
VIsual_mode = 'V';
- else if (*p_sel == 'e') {
- if (lt(curwin->w_cursor, VIsual))
- ++VIsual.col;
- else
- ++curwin->w_cursor.col;
+ } else if (*p_sel == 'e') {
+ if (lt(curwin->w_cursor, VIsual)) {
+ VIsual.col++;
+ } else {
+ curwin->w_cursor.col++;
+ }
}
}
}
@@ -3244,9 +3246,9 @@ void clear_showcmd(void)
top = curwin->w_cursor.lnum;
bot = VIsual.lnum;
}
- /* Include closed folds as a whole. */
- hasFolding(top, &top, NULL);
- hasFolding(bot, NULL, &bot);
+ // Include closed folds as a whole.
+ (void)hasFolding(top, &top, NULL);
+ (void)hasFolding(bot, NULL, &bot);
lines = bot - top + 1;
if (VIsual_mode == Ctrl_V) {
@@ -3782,7 +3784,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
int width1; /* text width for first screen line */
int width2; /* test width for wrapped screen line */
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
oap->inclusive = (curwin->w_curswant == MAXCOL);
col_off1 = curwin_col_off();
@@ -3927,6 +3929,8 @@ static void nv_mousescroll(cmdarg_T *cap)
cap->count0 = 3;
nv_scroll_line(cap);
}
+ } else {
+ mouse_scroll_horiz(cap->arg);
}
curwin->w_redr_status = true;
@@ -4053,16 +4057,15 @@ static void nv_zet(cmdarg_T *cap)
}
dozet:
- if (
- /* "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc"
- * and "zC" only in Visual mode. "zj" and "zk" are motion
- * commands. */
- cap->nchar != 'f' && cap->nchar != 'F'
- && !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar))
- && cap->nchar != 'j' && cap->nchar != 'k'
- &&
- checkclearop(cap->oap))
+ // "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc"
+ // and "zC" only in Visual mode. "zj" and "zk" are motion
+ // commands. */
+ if (cap->nchar != 'f' && cap->nchar != 'F'
+ && !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar))
+ && cap->nchar != 'j' && cap->nchar != 'k'
+ && checkclearop(cap->oap)) {
return;
+ }
/*
* For "z+", "z<CR>", "zt", "z.", "zz", "z^", "z-", "zb":
@@ -4099,6 +4102,7 @@ dozet:
case 't': scroll_cursor_top(0, true);
redraw_later(VALID);
+ set_fraction(curwin);
break;
/* "z." and "zz": put cursor in middle of screen */
@@ -4107,6 +4111,7 @@ dozet:
case 'z': scroll_cursor_halfway(true);
redraw_later(VALID);
+ set_fraction(curwin);
break;
/* "z^", "z-" and "zb": put cursor at bottom of screen */
@@ -4127,6 +4132,7 @@ dozet:
case 'b': scroll_cursor_bot(0, true);
redraw_later(VALID);
+ set_fraction(curwin);
break;
/* "zH" - scroll screen right half-page */
@@ -4460,8 +4466,8 @@ static void nv_colon(cmdarg_T *cap)
nv_operator(cap);
else {
if (cap->oap->op_type != OP_NOP) {
- /* Using ":" as a movement is characterwise exclusive. */
- cap->oap->motion_type = MCHAR;
+ // Using ":" as a movement is characterwise exclusive.
+ cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
} else if (cap->count0) {
/* translate "count:" into ":.,.+(count - 1)" */
@@ -4860,7 +4866,7 @@ static void nv_scroll(cmdarg_T *cap)
linenr_T lnum;
int half;
- cap->oap->motion_type = MLINE;
+ cap->oap->motion_type = kMTLineWise;
setpcmark();
if (cap->cmdchar == 'L') {
@@ -4940,7 +4946,7 @@ static void nv_right(cmdarg_T *cap)
return;
}
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
PAST_LINE = (VIsual_active && *p_sel != 'o');
@@ -5027,7 +5033,7 @@ static void nv_left(cmdarg_T *cap)
return;
}
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
for (n = cap->count1; n > 0; --n) {
if (oneleft() == false) {
@@ -5089,11 +5095,12 @@ static void nv_up(cmdarg_T *cap)
cap->arg = BACKWARD;
nv_page(cap);
} else {
- cap->oap->motion_type = MLINE;
- if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == false)
+ cap->oap->motion_type = kMTLineWise;
+ if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == false) {
clearopbeep(cap->oap);
- else if (cap->arg)
+ } else if (cap->arg) {
beginline(BL_WHITE | BL_FIX);
+ }
}
}
@@ -5107,23 +5114,24 @@ static void nv_down(cmdarg_T *cap)
/* <S-Down> is page down */
cap->arg = FORWARD;
nv_page(cap);
- } else
- /* In a quickfix window a <CR> jumps to the error under the cursor. */
- if (bt_quickfix(curbuf) && cap->cmdchar == CAR)
- if (curwin->w_llist_ref == NULL)
- do_cmdline_cmd(".cc"); /* quickfix window */
- else
- do_cmdline_cmd(".ll"); /* location list window */
- else {
- /* In the cmdline window a <CR> executes the command. */
- if (cmdwin_type != 0 && cap->cmdchar == CAR)
+ } else if (bt_quickfix(curbuf) && cap->cmdchar == CAR) {
+ // In a quickfix window a <CR> jumps to the error under the cursor.
+ if (curwin->w_llist_ref == NULL) {
+ do_cmdline_cmd(".cc"); // quickfix window
+ } else {
+ do_cmdline_cmd(".ll"); // location list window
+ }
+ } else {
+ // In the cmdline window a <CR> executes the command.
+ if (cmdwin_type != 0 && cap->cmdchar == CAR) {
cmdwin_result = CAR;
- else {
- cap->oap->motion_type = MLINE;
- if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == false)
+ } else {
+ cap->oap->motion_type = kMTLineWise;
+ if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == false) {
clearopbeep(cap->oap);
- else if (cap->arg)
+ } else if (cap->arg) {
beginline(BL_WHITE | BL_FIX);
+ }
}
}
}
@@ -5149,9 +5157,10 @@ static void nv_gotofile(cmdarg_T *cap)
ptr = grab_file_name(cap->count1, &lnum);
if (ptr != NULL) {
- /* do autowrite if necessary */
- if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf))
- autowrite(curbuf, false);
+ // do autowrite if necessary
+ if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf)) {
+ (void)autowrite(curbuf, false);
+ }
setpcmark();
(void)do_ecmd(0, ptr, NULL, NULL, ECMD_LAST,
P_HID(curbuf) ? ECMD_HIDE : 0, curwin);
@@ -5183,7 +5192,7 @@ static void nv_end(cmdarg_T *cap)
*/
static void nv_dollar(cmdarg_T *cap)
{
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = true;
/* In virtual mode when off the edge of a line and an operator
* is pending (whew!) keep the cursor where it is.
@@ -5258,18 +5267,19 @@ static int normal_search(
{
int i;
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
cap->oap->use_reg_one = true;
curwin->w_set_curswant = true;
i = do_search(cap->oap, dir, pat, cap->count1,
- opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL);
- if (i == 0)
+ opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL);
+ if (i == 0) {
clearop(cap->oap);
- else {
- if (i == 2)
- cap->oap->motion_type = MLINE;
+ } else {
+ if (i == 2) {
+ cap->oap->motion_type = kMTLineWise;
+ }
curwin->w_cursor.coladd = 0;
if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
foldOpenCursor();
@@ -5296,10 +5306,10 @@ static void nv_csearch(cmdarg_T *cap)
else
t_cmd = false;
- cap->oap->motion_type = MCHAR;
- if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == false)
+ cap->oap->motion_type = kMTCharWise;
+ if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == false) {
clearopbeep(cap->oap);
- else {
+ } else {
curwin->w_set_curswant = true;
/* Include a Tab for "tx" and for "dfx". */
if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD
@@ -5331,7 +5341,7 @@ static void nv_brackets(cmdarg_T *cap)
int findc;
int c;
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
old_pos = curwin->w_cursor;
curwin->w_cursor.coladd = 0; /* TODO: don't do this for an error. */
@@ -5621,11 +5631,11 @@ static void nv_percent(cmdarg_T *cap)
linenr_T lnum = curwin->w_cursor.lnum;
cap->oap->inclusive = true;
- if (cap->count0) { /* {cnt}% : goto {cnt} percentage in file */
- if (cap->count0 > 100)
+ if (cap->count0) { // {cnt}% : goto {cnt} percentage in file
+ if (cap->count0 > 100) {
clearopbeep(cap->oap);
- else {
- cap->oap->motion_type = MLINE;
+ } else {
+ cap->oap->motion_type = kMTLineWise;
setpcmark();
/* Round up, so CTRL-G will give same value. Watch out for a
* large line count, the line number must not go negative! */
@@ -5639,8 +5649,8 @@ static void nv_percent(cmdarg_T *cap)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
beginline(BL_SOL | BL_FIX);
}
- } else { /* "%" : go to matching paren */
- cap->oap->motion_type = MCHAR;
+ } else { // "%" : go to matching paren
+ cap->oap->motion_type = kMTCharWise;
cap->oap->use_reg_one = true;
if ((pos = findmatch(cap->oap, NUL)) == NULL)
clearopbeep(cap->oap);
@@ -5665,7 +5675,7 @@ static void nv_percent(cmdarg_T *cap)
*/
static void nv_brace(cmdarg_T *cap)
{
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
cap->oap->use_reg_one = true;
/* The motion used to be inclusive for "(", but that is not what Vi does. */
cap->oap->inclusive = false;
@@ -5699,7 +5709,7 @@ static void nv_mark(cmdarg_T *cap)
*/
static void nv_findpar(cmdarg_T *cap)
{
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
cap->oap->use_reg_one = true;
curwin->w_set_curswant = true;
@@ -5803,14 +5813,11 @@ static void nv_replace(cmdarg_T *cap)
return;
}
- /*
- * Replacing with a TAB is done by edit() when it is complicated because
- * 'expandtab' or 'smarttab' is set. CTRL-V TAB inserts a literal TAB.
- * Other characters are done below to avoid problems with things like
- * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
- */
- if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' &&
- (curbuf->b_p_et || p_sta)) {
+ // Replacing with a TAB is done by edit() when it is complicated because
+ // 'expandtab' or 'smarttab' is set. CTRL-V TAB inserts a literal TAB.
+ // Other characters are done below to avoid problems with things like
+ // CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
+ if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && (curbuf->b_p_et || p_sta)) {
stuffnumReadbuff(cap->count1);
stuffcharReadbuff('R');
stuffcharReadbuff('\t');
@@ -6077,10 +6084,11 @@ static void nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos)
else
check_cursor();
}
- cap->oap->motion_type = flag ? MLINE : MCHAR;
- if (cap->cmdchar == '`')
+ cap->oap->motion_type = flag ? kMTLineWise : kMTCharWise;
+ if (cap->cmdchar == '`') {
cap->oap->use_reg_one = true;
- cap->oap->inclusive = false; /* ignored if not MCHAR */
+ }
+ cap->oap->inclusive = false; // ignored if not kMTCharWise
curwin->w_set_curswant = true;
}
@@ -6557,7 +6565,7 @@ static void nv_g_cmd(cmdarg_T *cap)
if (!curwin->w_p_wrap
|| hasFolding(curwin->w_cursor.lnum, NULL, NULL)
) {
- oap->motion_type = MLINE;
+ oap->motion_type = kMTLineWise;
i = cursor_down(cap->count1, oap->op_type == OP_NOP);
} else
i = nv_screengo(oap, FORWARD, cap->count1);
@@ -6572,7 +6580,7 @@ static void nv_g_cmd(cmdarg_T *cap)
if (!curwin->w_p_wrap
|| hasFolding(curwin->w_cursor.lnum, NULL, NULL)
) {
- oap->motion_type = MLINE;
+ oap->motion_type = kMTLineWise;
i = cursor_up(cap->count1, oap->op_type == OP_NOP);
} else
i = nv_screengo(oap, BACKWARD, cap->count1);
@@ -6599,7 +6607,7 @@ static void nv_g_cmd(cmdarg_T *cap)
case 'm':
case K_HOME:
case K_KHOME:
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
oap->inclusive = false;
if (curwin->w_p_wrap
&& curwin->w_width != 0
@@ -6632,7 +6640,7 @@ static void nv_g_cmd(cmdarg_T *cap)
case '_':
/* "g_": to the last non-blank character in the line or <count> lines
* downward. */
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = true;
curwin->w_curswant = MAXCOL;
if (cursor_down(cap->count1 - 1,
@@ -6660,7 +6668,7 @@ static void nv_g_cmd(cmdarg_T *cap)
{
int col_off = curwin_col_off();
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
oap->inclusive = true;
if (curwin->w_p_wrap
&& curwin->w_width != 0
@@ -6722,23 +6730,19 @@ static void nv_g_cmd(cmdarg_T *cap)
*/
case 'e':
case 'E':
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
curwin->w_set_curswant = true;
oap->inclusive = true;
if (bckend_word(cap->count1, cap->nchar == 'E', false) == false)
clearopbeep(oap);
break;
- /*
- * "g CTRL-G": display info about cursor position
- */
+ // "g CTRL-G": display info about cursor position
case Ctrl_G:
- cursor_pos_info();
+ cursor_pos_info(NULL);
break;
- /*
- * "gi": start Insert at the last position.
- */
+ // "gi": start Insert at the last position.
case 'i':
if (curbuf->b_last_insert.mark.lnum != 0) {
curwin->w_cursor = curbuf->b_last_insert.mark;
@@ -6955,10 +6959,16 @@ static void n_opencmd(cmdarg_T *cap)
(cap->cmdchar == 'o' ? 1 : 0))
)
&& open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
- has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM :
- 0, 0)) {
- if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum)
+ has_format_option(FO_OPEN_COMS)
+ ? OPENLINE_DO_COM : 0,
+ 0)) {
+ if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum) {
update_single_line(curwin, oldline);
+ }
+ if (curwin->w_p_cul) {
+ // force redraw of cursorline
+ curwin->w_valid &= ~VALID_CROW;
+ }
invoke_edit(cap, false, cap->cmdchar, true);
}
}
@@ -7048,18 +7058,17 @@ static void nv_operator(cmdarg_T *cap)
*/
static void set_op_var(int optype)
{
- char_u opchars[3];
-
- if (optype == OP_NOP)
+ if (optype == OP_NOP) {
set_vim_var_string(VV_OP, NULL, 0);
- else {
+ } else {
+ char opchars[3];
int opchar0 = get_op_char(optype);
assert(opchar0 >= 0 && opchar0 <= UCHAR_MAX);
- opchars[0] = (char_u)opchar0;
+ opchars[0] = (char) opchar0;
int opchar1 = get_extra_op_char(optype);
assert(opchar1 >= 0 && opchar1 <= UCHAR_MAX);
- opchars[1] = (char_u)opchar1;
+ opchars[1] = (char) opchar1;
opchars[2] = NUL;
set_vim_var_string(VV_OP, opchars, -1);
@@ -7077,17 +7086,19 @@ static void set_op_var(int optype)
*/
static void nv_lineop(cmdarg_T *cap)
{
- cap->oap->motion_type = MLINE;
- if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == false)
+ cap->oap->motion_type = kMTLineWise;
+ if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == false) {
clearopbeep(cap->oap);
- else if ( (cap->oap->op_type == OP_DELETE /* only with linewise motions */
+ } else if ((cap->oap->op_type == OP_DELETE
+ // only with linewise motions
&& cap->oap->motion_force != 'v'
&& cap->oap->motion_force != Ctrl_V)
|| cap->oap->op_type == OP_LSHIFT
- || cap->oap->op_type == OP_RSHIFT)
+ || cap->oap->op_type == OP_RSHIFT) {
beginline(BL_SOL | BL_FIX);
- else if (cap->oap->op_type != OP_YANK) /* 'Y' does not move cursor */
+ } else if (cap->oap->op_type != OP_YANK) { // 'Y' does not move cursor
beginline(BL_WHITE | BL_FIX);
+ }
}
/*
@@ -7111,7 +7122,7 @@ static void nv_home(cmdarg_T *cap)
*/
static void nv_pipe(cmdarg_T *cap)
{
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
beginline(0);
if (cap->count0 > 0) {
@@ -7130,7 +7141,7 @@ static void nv_pipe(cmdarg_T *cap)
*/
static void nv_bck_word(cmdarg_T *cap)
{
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
curwin->w_set_curswant = true;
if (bck_word(cap->count1, cap->arg, false) == false)
@@ -7179,7 +7190,7 @@ static void nv_wordcmd(cmdarg_T *cap)
}
}
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
curwin->w_set_curswant = true;
if (word_end)
n = end_word(cap->count1, cap->arg, flag, false);
@@ -7230,7 +7241,7 @@ static void adjust_cursor(oparg_T *oap)
*/
static void nv_beginline(cmdarg_T *cap)
{
- cap->oap->motion_type = MCHAR;
+ cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
beginline(cap->arg);
if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP)
@@ -7309,7 +7320,7 @@ static void nv_goto(cmdarg_T *cap)
lnum = curbuf->b_ml.ml_line_count;
else
lnum = 1L;
- cap->oap->motion_type = MLINE;
+ cap->oap->motion_type = kMTLineWise;
setpcmark();
/* When a count is given, use it instead of the default lnum */
@@ -7637,19 +7648,25 @@ static void nv_halfpage(cmdarg_T *cap)
*/
static void nv_join(cmdarg_T *cap)
{
- if (VIsual_active) /* join the visual lines */
+ if (VIsual_active) { // join the visual lines
nv_operator(cap);
- else if (!checkclearop(cap->oap)) {
- if (cap->count0 <= 1)
- cap->count0 = 2; /* default for join is two lines! */
+ } else if (!checkclearop(cap->oap)) {
+ if (cap->count0 <= 1) {
+ cap->count0 = 2; // default for join is two lines!
+ }
if (curwin->w_cursor.lnum + cap->count0 - 1 >
- curbuf->b_ml.ml_line_count)
- clearopbeep(cap->oap); /* beyond last line */
- else {
- prep_redo(cap->oap->regname, cap->count0,
- NUL, cap->cmdchar, NUL, NUL, cap->nchar);
- do_join(cap->count0, cap->nchar == NUL, true, true, true);
+ curbuf->b_ml.ml_line_count) {
+ // can't join when on the last line
+ if (cap->count0 <= 2) {
+ clearopbeep(cap->oap);
+ return;
+ }
+ cap->count0 = curbuf->b_ml.ml_line_count - curwin->w_cursor.lnum + 1;
}
+
+ prep_redo(cap->oap->regname, cap->count0,
+ NUL, cap->cmdchar, NUL, NUL, cap->nchar);
+ do_join(cap->count0, cap->nchar == NUL, true, true, true);
}
}
@@ -7742,6 +7759,10 @@ static void nv_put(cmdarg_T *cap)
if (was_visual) {
curbuf->b_visual.vi_start = curbuf->b_op_start;
curbuf->b_visual.vi_end = curbuf->b_op_end;
+ // need to adjust cursor position
+ if (*p_sel == 'e') {
+ inc(&curbuf->b_visual.vi_end);
+ }
}
/* When all lines were selected and deleted do_put() leaves an empty
@@ -7776,7 +7797,7 @@ static void nv_open(cmdarg_T *cap)
n_opencmd(cap);
}
-// calculate start/end virtual columns for operating in block mode
+// Calculate start/end virtual columns for operating in block mode.
static void get_op_vcol(
oparg_T *oap,
colnr_T redo_VIsual_vcol,
@@ -7786,11 +7807,12 @@ static void get_op_vcol(
colnr_T start;
colnr_T end;
- if (VIsual_mode != Ctrl_V) {
+ if (VIsual_mode != Ctrl_V
+ || (!initial && oap->end.col < curwin->w_width)) {
return;
}
- oap->motion_type = MBLOCK;
+ oap->motion_type = kMTBlockWise;
// prevent from moving onto a trail byte
if (has_mbyte) {
@@ -7798,20 +7820,23 @@ static void get_op_vcol(
}
getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol);
- getvvcol(curwin, &(oap->end), &start, NULL, &end);
+ if (!redo_VIsual_busy) {
+ getvvcol(curwin, &(oap->end), &start, NULL, &end);
- if (start < oap->start_vcol) {
- oap->start_vcol = start;
- }
- if (end > oap->end_vcol) {
- if (initial && *p_sel == 'e'
- && start >= 1
- && start - 1 >= oap->end_vcol) {
- oap->end_vcol = start - 1;
- } else {
- oap->end_vcol = end;
+ if (start < oap->start_vcol) {
+ oap->start_vcol = start;
+ }
+ if (end > oap->end_vcol) {
+ if (initial && *p_sel == 'e'
+ && start >= 1
+ && start - 1 >= oap->end_vcol) {
+ oap->end_vcol = start - 1;
+ } else {
+ oap->end_vcol = end;
+ }
}
}
+
// if '$' was used, get oap->end_vcol from longest line
if (curwin->w_curswant == MAXCOL) {
curwin->w_cursor.col = MAXCOL;
diff --git a/src/nvim/normal.h b/src/nvim/normal.h
index 95619c7ef6..51170105ed 100644
--- a/src/nvim/normal.h
+++ b/src/nvim/normal.h
@@ -10,18 +10,29 @@
#define FIND_STRING 2 /* find any string (WORD) */
#define FIND_EVAL 4 /* include "->", "[]" and "." */
+/// Motion types, used for operators and for yank/delete registers.
+///
+/// The three valid numerical values must not be changed, as they
+/// are used in external communication and serialization.
+typedef enum {
+ kMTCharWise = 0, ///< character-wise movement/register
+ kMTLineWise = 1, ///< line-wise movement/register
+ kMTBlockWise = 2, ///< block-wise movement/register
+ kMTUnknown = -1 ///< Unknown or invalid motion type
+} MotionType;
+
/*
* Arguments for operators.
*/
typedef struct oparg_S {
int op_type; // current pending operator type
int regname; // register to use for the operator
- int motion_type; // type of the current cursor motion
+ MotionType motion_type; // type of the current cursor motion
int motion_force; // force motion type: 'v', 'V' or CTRL-V
bool use_reg_one; // true if delete uses reg 1 even when not
// linewise
bool inclusive; // true if char motion is inclusive (only
- // valid when motion_type is MCHAR)
+ // valid when motion_type is kMTCharWise)
bool end_adjusted; // backuped b_op_end one char (only used by
// do_format())
pos_T start; // start of the operator
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 7614e6365a..adfd0424f0 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -19,6 +19,7 @@
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_getln.h"
+#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/indent.h"
@@ -190,7 +191,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
(linenr_T)(oap->end.lnum + 1)) == FAIL)
return;
- if (oap->motion_type == MBLOCK) {
+ if (oap->motion_type == kMTBlockWise) {
block_col = curwin->w_cursor.col;
}
@@ -198,7 +199,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
first_char = *get_cursor_line_ptr();
if (first_char == NUL) { // empty line
curwin->w_cursor.col = 0;
- } else if (oap->motion_type == MBLOCK) {
+ } else if (oap->motion_type == kMTBlockWise) {
shift_block(oap, amount);
} else if (first_char != '#' || !preprocs_left()) {
// Move the line right if it doesn't start with '#', 'smartindent'
@@ -212,7 +213,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
/* The cursor line is not in a closed fold */
foldOpenCursor();
- if (oap->motion_type == MBLOCK) {
+ if (oap->motion_type == kMTBlockWise) {
curwin->w_cursor.lnum = oap->start.lnum;
curwin->w_cursor.col = block_col;
} else if (curs_top) { /* put cursor on first line, for ">>" */
@@ -810,17 +811,17 @@ yankreg_T *copy_register(int name)
return copy;
}
-/*
- * return TRUE if the current yank register has type MLINE
- */
-int yank_register_mline(int regname)
+/// check if the current yank register has kMTLineWise register type
+bool yank_register_mline(int regname)
{
- if (regname != 0 && !valid_yank_reg(regname, false))
- return FALSE;
- if (regname == '_') /* black hole is always empty */
- return FALSE;
+ if (regname != 0 && !valid_yank_reg(regname, false)) {
+ return false;
+ }
+ if (regname == '_') { // black hole is always empty
+ return false;
+ }
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
- return reg->y_type == MLINE;
+ return reg->y_type == kMTLineWise;
}
/*
@@ -835,12 +836,13 @@ int do_record(int c)
yankreg_T *old_y_previous;
int retval;
- if (Recording == FALSE) { /* start recording */
- /* registers 0-9, a-z and " are allowed */
- if (c < 0 || (!ASCII_ISALNUM(c) && c != '"'))
+ if (Recording == false) {
+ // start recording
+ // registers 0-9, a-z and " are allowed
+ if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) {
retval = FAIL;
- else {
- Recording = TRUE;
+ } else {
+ Recording = c;
showmode();
regname = c;
retval = OK;
@@ -917,7 +919,7 @@ static int stuff_yank(int regname, char_u *p)
reg->y_array = (char_u **)xmalloc(sizeof(char_u *));
reg->y_array[0] = p;
reg->y_size = 1;
- reg->y_type = MCHAR; /* used to be MLINE, why? */
+ reg->y_type = kMTCharWise;
}
reg->timestamp = os_time();
return OK;
@@ -1009,8 +1011,8 @@ do_execreg (
for (i = reg->y_size - 1; i >= 0; i--) {
char_u *escaped;
- /* insert NL between lines and after last line if type is MLINE */
- if (reg->y_type == MLINE || i < reg->y_size - 1
+ // insert NL between lines and after last line if type is kMTLineWise
+ if (reg->y_type == kMTLineWise || i < reg->y_size - 1
|| addcr) {
if (ins_typebuf((char_u *)"\n", remap, 0, TRUE, silent) == FAIL)
return FAIL;
@@ -1135,12 +1137,11 @@ insert_reg (
else {
for (i = 0; i < reg->y_size; i++) {
stuffescaped(reg->y_array[i], literally);
- /*
- * Insert a newline between lines and after last line if
- * y_type is MLINE.
- */
- if (reg->y_type == MLINE || i < reg->y_size - 1)
+ // Insert a newline between lines and after last line if
+ // y_type is kMTLineWise.
+ if (reg->y_type == kMTLineWise || i < reg->y_size - 1) {
stuffcharReadbuff('\n');
+ }
}
}
}
@@ -1259,21 +1260,18 @@ get_spec_reg (
return FALSE;
}
-/*
- * Paste a yank register into the command line.
- * Only for non-special registers.
- * Used by CTRL-R command in command-line mode
- * insert_reg() can't be used here, because special characters from the
- * register contents will be interpreted as commands.
- *
- * return FAIL for failure, OK otherwise
- */
-int
-cmdline_paste_reg (
- int regname,
- int literally, /* Insert text literally instead of "as typed" */
- int remcr /* don't add trailing CR */
-)
+/// Paste a yank register into the command line.
+/// Only for non-special registers.
+/// Used by CTRL-R command in command-line mode
+/// insert_reg() can't be used here, because special characters from the
+/// register contents will be interpreted as commands.
+///
+/// @param regname Register name.
+/// @param literally Insert text literally instead of "as typed".
+/// @param remcr When true, don't add CR characters.
+///
+/// @returns FAIL for failure, OK otherwise
+bool cmdline_paste_reg(int regname, bool literally, bool remcr)
{
long i;
@@ -1284,13 +1282,9 @@ cmdline_paste_reg (
for (i = 0; i < reg->y_size; i++) {
cmdline_paste_str(reg->y_array[i], literally);
- /* Insert ^M between lines and after last line if type is MLINE.
- * Don't do this when "remcr" is TRUE and the next line is empty. */
- if (reg->y_type == MLINE
- || (i < reg->y_size - 1
- && !(remcr
- && i == reg->y_size - 2
- && *reg->y_array[i + 1] == NUL))) {
+ // Insert ^M between lines and after last line if type is kMTLineWise.
+ // Don't do this when "remcr" is true.
+ if ((reg->y_type == kMTLineWise || i < reg->y_size - 1) && !remcr) {
cmdline_paste_str((char_u *)"\r", literally);
}
@@ -1334,10 +1328,10 @@ int op_delete(oparg_T *oap)
/*
* Imitate the strange Vi behaviour: If the delete spans more than one
- * line and motion_type == MCHAR and the result is a blank line, make the
+ * line and motion_type == kMTCharWise and the result is a blank line, make the
* delete linewise. Don't do this for the change command or Visual mode.
*/
- if (oap->motion_type == MCHAR
+ if (oap->motion_type == kMTCharWise
&& !oap->is_VIsual
&& oap->line_count > 1
&& oap->motion_force == NUL
@@ -1346,15 +1340,16 @@ int op_delete(oparg_T *oap)
if (*ptr != NUL)
ptr += oap->inclusive;
ptr = skipwhite(ptr);
- if (*ptr == NUL && inindent(0))
- oap->motion_type = MLINE;
+ if (*ptr == NUL && inindent(0)) {
+ oap->motion_type = kMTLineWise;
+ }
}
/*
* Check for trying to delete (e.g. "D") in an empty line.
* Note: For the change operator it is ok.
*/
- if (oap->motion_type != MLINE
+ if (oap->motion_type != kMTLineWise
&& oap->line_count == 1
&& oap->op_type == OP_DELETE
&& *ml_get(oap->start.lnum) == NUL) {
@@ -1390,7 +1385,7 @@ int op_delete(oparg_T *oap)
* Put deleted text into register 1 and shift number registers if the
* delete contains a line break, or when a regname has been specified.
*/
- if (oap->regname != 0 || oap->motion_type == MLINE
+ if (oap->regname != 0 || oap->motion_type == kMTLineWise
|| oap->line_count > 1 || oap->use_reg_one) {
free_register(&y_regs[9]); /* free register "9 */
for (n = 9; n > 1; n--)
@@ -1403,14 +1398,15 @@ int op_delete(oparg_T *oap)
/* Yank into small delete register when no named register specified
* and the delete is within one line. */
- if (oap->regname == 0 && oap->motion_type != MLINE
+ if (oap->regname == 0 && oap->motion_type != kMTLineWise
&& oap->line_count == 1) {
reg = get_yank_register('-', YREG_YANK);
op_yank_reg(oap, false, reg, false);
}
- if(oap->regname == 0) {
+ if (oap->regname == 0) {
set_clipboard(0, reg);
+ yank_do_autocmd(oap, reg);
}
}
@@ -1418,7 +1414,7 @@ int op_delete(oparg_T *oap)
/*
* block mode delete
*/
- if (oap->motion_type == MBLOCK) {
+ if (oap->motion_type == kMTBlockWise) {
if (u_save((linenr_T)(oap->start.lnum - 1),
(linenr_T)(oap->end.lnum + 1)) == FAIL) {
return FAIL;
@@ -1456,9 +1452,9 @@ int op_delete(oparg_T *oap)
check_cursor_col();
changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col,
- oap->end.lnum + 1, 0L);
- oap->line_count = 0; /* no lines deleted */
- } else if (oap->motion_type == MLINE) {
+ oap->end.lnum + 1, 0L);
+ oap->line_count = 0; // no lines deleted
+ } else if (oap->motion_type == kMTLineWise) {
if (oap->op_type == OP_CHANGE) {
/* Delete the lines except the first one. Temporarily move the
* cursor to the next line. Save the current line number, if the
@@ -1555,62 +1551,38 @@ int op_delete(oparg_T *oap)
if (gchar_cursor() != NUL)
curwin->w_cursor.coladd = 0;
}
- if (oap->op_type == OP_DELETE
- && oap->inclusive
- && oap->end.lnum == curbuf->b_ml.ml_line_count
- && n > (int)STRLEN(ml_get(oap->end.lnum))) {
- /* Special case: gH<Del> deletes the last line. */
- del_lines(1L, FALSE);
- } else {
- (void)del_bytes((long)n, !virtual_op, oap->op_type == OP_DELETE
- && !oap->is_VIsual
- );
- }
- } else { /* delete characters between lines */
+
+ (void)del_bytes((long)n, !virtual_op,
+ oap->op_type == OP_DELETE && !oap->is_VIsual);
+ } else {
+ // delete characters between lines
pos_T curpos;
- int delete_last_line;
/* save deleted and changed lines for undo */
if (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
(linenr_T)(curwin->w_cursor.lnum + oap->line_count)) == FAIL)
return FAIL;
- delete_last_line = (oap->end.lnum == curbuf->b_ml.ml_line_count);
- truncate_line(TRUE); /* delete from cursor to end of line */
-
- curpos = curwin->w_cursor; /* remember curwin->w_cursor */
- ++curwin->w_cursor.lnum;
- del_lines(oap->line_count - 2, FALSE);
+ truncate_line(true); // delete from cursor to end of line
- if (delete_last_line)
- oap->end.lnum = curbuf->b_ml.ml_line_count;
+ curpos = curwin->w_cursor; // remember curwin->w_cursor
+ curwin->w_cursor.lnum++;
+ del_lines(oap->line_count - 2, false);
+ // delete from start of line until op_end
n = (oap->end.col + 1 - !oap->inclusive);
- if (oap->inclusive && delete_last_line
- && n > (int)STRLEN(ml_get(oap->end.lnum))) {
- /* Special case: gH<Del> deletes the last line. */
- del_lines(1L, FALSE);
- curwin->w_cursor = curpos; /* restore curwin->w_cursor */
- if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
- curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- } else {
- /* delete from start of line until op_end */
- curwin->w_cursor.col = 0;
- (void)del_bytes((long)n, !virtual_op, oap->op_type == OP_DELETE
- && !oap->is_VIsual
- );
- curwin->w_cursor = curpos; /* restore curwin->w_cursor */
- }
- if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- do_join(2, FALSE, FALSE, FALSE, false);
- }
+ curwin->w_cursor.col = 0;
+ (void)del_bytes((long)n, !virtual_op,
+ oap->op_type == OP_DELETE && !oap->is_VIsual);
+ curwin->w_cursor = curpos; // restore curwin->w_cursor
+ (void)do_join(2, false, false, false, false);
}
}
msgmore(curbuf->b_ml.ml_line_count - old_lcount);
setmarks:
- if (oap->motion_type == MBLOCK) {
+ if (oap->motion_type == kMTBlockWise) {
curbuf->b_op_end.lnum = oap->end.lnum;
curbuf->b_op_end.col = oap->start.col;
} else
@@ -1671,7 +1643,7 @@ int op_replace(oparg_T *oap, int c)
/*
* block mode replace
*/
- if (oap->motion_type == MBLOCK) {
+ if (oap->motion_type == kMTBlockWise) {
bd.is_MAX = (curwin->w_curswant == MAXCOL);
for (; curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) {
curwin->w_cursor.col = 0; /* make sure cursor position is valid */
@@ -1760,10 +1732,8 @@ int op_replace(oparg_T *oap, int c)
}
}
} else {
- /*
- * MCHAR and MLINE motion replace.
- */
- if (oap->motion_type == MLINE) {
+ // Characterwise or linewise motion replace.
+ if (oap->motion_type == kMTLineWise) {
oap->start.col = 0;
curwin->w_cursor.col = 0;
oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
@@ -1853,8 +1823,8 @@ void op_tilde(oparg_T *oap)
return;
pos = oap->start;
- if (oap->motion_type == MBLOCK) { // Visual block mode
- for (; pos.lnum <= oap->end.lnum; ++pos.lnum) {
+ if (oap->motion_type == kMTBlockWise) { // Visual block mode
+ for (; pos.lnum <= oap->end.lnum; pos.lnum++) {
int one_change;
block_prep(oap, &bd, pos.lnum, FALSE);
@@ -1865,8 +1835,8 @@ void op_tilde(oparg_T *oap)
}
if (did_change)
changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L);
- } else { /* not block mode */
- if (oap->motion_type == MLINE) {
+ } else { // not block mode
+ if (oap->motion_type == kMTLineWise) {
oap->start.col = 0;
pos.col = 0;
oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
@@ -2016,7 +1986,7 @@ void op_insert(oparg_T *oap, long count1)
curwin->w_cursor.lnum = oap->start.lnum;
update_screen(INVERTED);
- if (oap->motion_type == MBLOCK) {
+ if (oap->motion_type == kMTBlockWise) {
// When 'virtualedit' is used, need to insert the extra spaces before
// doing block_prep(). When only "block" is used, virtual edit is
// already disabled, but still need it when calling
@@ -2042,7 +2012,7 @@ void op_insert(oparg_T *oap, long count1)
}
if (oap->op_type == OP_APPEND) {
- if (oap->motion_type == MBLOCK
+ if (oap->motion_type == kMTBlockWise
&& curwin->w_cursor.coladd == 0
) {
/* Move the cursor to the character right of the block. */
@@ -2076,8 +2046,8 @@ void op_insert(oparg_T *oap, long count1)
// When a tab was inserted, and the characters in front of the tab
// have been converted to a tab as well, the column of the cursor
// might have actually been reduced, so need to adjust here. */
- if (t1.lnum == curbuf->b_op_start_orig.lnum &&
- lt(curbuf->b_op_start_orig, t1)) {
+ if (t1.lnum == curbuf->b_op_start_orig.lnum
+ && lt(curbuf->b_op_start_orig, t1)) {
oap->start = curbuf->b_op_start_orig;
}
@@ -2087,7 +2057,7 @@ void op_insert(oparg_T *oap, long count1)
if (curwin->w_cursor.lnum != oap->start.lnum || got_int)
return;
- if (oap->motion_type == MBLOCK) {
+ if (oap->motion_type == kMTBlockWise) {
struct block_def bd2;
/* The user may have moved the cursor before inserting something, try
@@ -2174,7 +2144,7 @@ int op_change(oparg_T *oap)
struct block_def bd;
l = oap->start.col;
- if (oap->motion_type == MLINE) {
+ if (oap->motion_type == kMTLineWise) {
l = 0;
if (!p_paste && curbuf->b_p_si
&& !curbuf->b_p_cin
@@ -2196,7 +2166,7 @@ int op_change(oparg_T *oap)
// check for still on same line (<CR> in inserted text meaningless)
// skip blank lines too
- if (oap->motion_type == MBLOCK) {
+ if (oap->motion_type == kMTBlockWise) {
// Add spaces before getting the current line length.
if (virtual_op && (curwin->w_cursor.coladd > 0
|| gchar_cursor() == NUL)) {
@@ -2208,8 +2178,9 @@ int op_change(oparg_T *oap)
bd.textcol = curwin->w_cursor.col;
}
- if (oap->motion_type == MLINE)
+ if (oap->motion_type == kMTLineWise) {
fix_indent();
+ }
retval = edit(NUL, FALSE, (linenr_T)1);
@@ -2218,7 +2189,7 @@ int op_change(oparg_T *oap)
* block.
* Don't repeat the insert when Insert mode ended with CTRL-C.
*/
- if (oap->motion_type == MBLOCK
+ if (oap->motion_type == kMTBlockWise
&& oap->start.lnum != oap->end.lnum && !got_int) {
// Auto-indenting may have changed the indent. If the cursor was past
// the indent, exclude that indent change from the inserted text.
@@ -2333,6 +2304,8 @@ bool op_yank(oparg_T *oap, bool message)
yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK);
op_yank_reg(oap, message, reg, is_append_register(oap->regname));
set_clipboard(oap->regname, reg);
+ yank_do_autocmd(oap, reg);
+
return true;
}
@@ -2344,7 +2317,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
char_u **new_ptr;
linenr_T lnum; /* current line number */
long j;
- int yanktype = oap->motion_type;
+ MotionType yank_type = oap->motion_type;
long yanklines = oap->line_count;
linenr_T yankendlnum = oap->end.lnum;
char_u *p;
@@ -2362,19 +2335,19 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
* If the cursor was in column 1 before and after the movement, and the
* operator is not inclusive, the yank is always linewise.
*/
- if (oap->motion_type == MCHAR
+ if (oap->motion_type == kMTCharWise
&& oap->start.col == 0
&& !oap->inclusive
&& (!oap->is_VIsual || *p_sel == 'o')
&& oap->end.col == 0
&& yanklines > 1) {
- yanktype = MLINE;
- --yankendlnum;
- --yanklines;
+ yank_type = kMTLineWise;
+ yankendlnum--;
+ yanklines--;
}
reg->y_size = yanklines;
- reg->y_type = yanktype; /* set the yank register type */
+ reg->y_type = yank_type; // set the yank register type
reg->y_width = 0;
reg->y_array = xcalloc(yanklines, sizeof(char_u *));
reg->additional_data = NULL;
@@ -2383,7 +2356,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
y_idx = 0;
lnum = oap->start.lnum;
- if (yanktype == MBLOCK) {
+ if (yank_type == kMTBlockWise) {
// Visual block mode
reg->y_width = oap->end_vcol - oap->start_vcol;
@@ -2393,16 +2366,16 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
for (; lnum <= yankendlnum; lnum++, y_idx++) {
switch (reg->y_type) {
- case MBLOCK:
- block_prep(oap, &bd, lnum, FALSE);
+ case kMTBlockWise:
+ block_prep(oap, &bd, lnum, false);
yank_copy_line(reg, &bd, y_idx);
break;
- case MLINE:
+ case kMTLineWise:
reg->y_array[y_idx] = vim_strsave(ml_get(lnum));
break;
- case MCHAR:
+ case kMTCharWise:
{
colnr_T startcol = 0, endcol = MAXCOL;
int is_oneChar = FALSE;
@@ -2463,7 +2436,9 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
yank_copy_line(reg, &bd, y_idx);
break;
}
- /* NOTREACHED */
+ // NOTREACHED
+ case kMTUnknown:
+ assert(false);
}
}
@@ -2474,12 +2449,15 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
xfree(curr->y_array);
curr->y_array = new_ptr;
- if (yanktype == MLINE) /* MLINE overrides MCHAR and MBLOCK */
- curr->y_type = MLINE;
+ if (yank_type == kMTLineWise) {
+ // kMTLineWise overrides kMTCharWise and kMTBlockWise
+ curr->y_type = kMTLineWise;
+ }
- /* Concatenate the last line of the old block with the first line of
- * the new block, unless being Vi compatible. */
- if (curr->y_type == MCHAR && vim_strchr(p_cpo, CPO_REGAPPEND) == NULL) {
+ // Concatenate the last line of the old block with the first line of
+ // the new block, unless being Vi compatible.
+ if (curr->y_type == kMTCharWise
+ && vim_strchr(p_cpo, CPO_REGAPPEND) == NULL) {
pnew = xmalloc(STRLEN(curr->y_array[curr->y_size - 1])
+ STRLEN(reg->y_array[0]) + 1);
STRCPY(pnew, curr->y_array[--j]);
@@ -2499,7 +2477,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
redraw_later(SOME_VALID); // cursor moved to start
}
if (message) { // Display message about yank?
- if (yanktype == MCHAR && yanklines == 1) {
+ if (yank_type == kMTCharWise && yanklines == 1) {
yanklines = 0;
}
// Some versions of Vi use ">=" here, some don't...
@@ -2507,12 +2485,12 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
// redisplay now, so message is not deleted
update_topline_redraw();
if (yanklines == 1) {
- if (yanktype == MBLOCK) {
+ if (yank_type == kMTBlockWise) {
MSG(_("block of 1 line yanked"));
} else {
MSG(_("1 line yanked"));
}
- } else if (yanktype == MBLOCK) {
+ } else if (yank_type == kMTBlockWise) {
smsg(_("block of %" PRId64 " lines yanked"),
(int64_t)yanklines);
} else {
@@ -2526,7 +2504,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
*/
curbuf->b_op_start = oap->start;
curbuf->b_op_end = oap->end;
- if (yanktype == MLINE) {
+ if (yank_type == kMTLineWise) {
curbuf->b_op_start.col = 0;
curbuf->b_op_end.col = MAXCOL;
}
@@ -2548,6 +2526,58 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, long y_idx)
*pnew = NUL;
}
+/// Execute autocommands for TextYankPost.
+///
+/// @param oap Operator arguments.
+/// @param reg The yank register used.
+static void yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
+ FUNC_ATTR_NONNULL_ALL
+{
+ static bool recursive = false;
+
+ if (recursive || !has_event(EVENT_TEXTYANKPOST)) {
+ // No autocommand was defined
+ // or we yanked from this autocommand.
+ return;
+ }
+
+ recursive = true;
+
+ // set v:event to a dictionary with information about the yank
+ dict_T *dict = get_vim_var_dict(VV_EVENT);
+
+ // the yanked text
+ list_T *list = list_alloc();
+ for (linenr_T i = 0; i < reg->y_size; i++) {
+ list_append_string(list, reg->y_array[i], -1);
+ }
+ list->lv_lock = VAR_FIXED;
+ dict_add_list(dict, "regcontents", list);
+
+ // the register type
+ char buf[NUMBUFLEN+2];
+ format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf));
+ dict_add_nr_str(dict, "regtype", 0, (char_u *)buf);
+
+ // name of requested register or the empty string for an unnamed operation.
+ buf[0] = (char)oap->regname;
+ buf[1] = NUL;
+ dict_add_nr_str(dict, "regname", 0, (char_u *)buf);
+
+ // kind of operation (yank/delete/change)
+ buf[0] = get_op_char(oap->op_type);
+ buf[1] = NUL;
+ dict_add_nr_str(dict, "operator", 0, (char_u *)buf);
+
+ dict_set_keys_readonly(dict);
+ textlock++;
+ apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf);
+ textlock--;
+ dict_clear(dict);
+
+ recursive = false;
+}
+
/*
* Put contents of register "regname" into the text.
@@ -2564,8 +2594,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
int totlen = 0; /* init for gcc */
linenr_T lnum;
colnr_T col;
- long i; /* index in y_array[] */
- int y_type;
+ long i; // index in y_array[]
+ MotionType y_type;
long y_size;
int oldlen;
long y_width = 0;
@@ -2626,7 +2656,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
if (insert_string != NULL) {
- y_type = MCHAR;
+ y_type = kMTCharWise;
if (regname == '=') {
/* For the = register we need to split the string at NL
* characters.
@@ -2645,7 +2675,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
++ptr;
/* A trailing '\n' makes the register linewise. */
if (*ptr == NUL) {
- y_type = MLINE;
+ y_type = kMTLineWise;
break;
}
}
@@ -2686,19 +2716,29 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
return;
}
- if (y_type == MLINE) {
+ if (y_type == kMTLineWise) {
if (flags & PUT_LINE_SPLIT) {
- /* "p" or "P" in Visual mode: split the lines to put the text in
- * between. */
- if (u_save_cursor() == FAIL)
+ // "p" or "P" in Visual mode: split the lines to put the text in
+ // between.
+ if (u_save_cursor() == FAIL) {
goto end;
- ptr = vim_strsave(get_cursor_pos_ptr());
- ml_append(curwin->w_cursor.lnum, ptr, (colnr_T)0, FALSE);
+ }
+ char_u *p = get_cursor_pos_ptr();
+ if (dir == FORWARD && *p != NUL) {
+ mb_ptr_adv(p);
+ }
+ ptr = vim_strsave(p);
+ ml_append(curwin->w_cursor.lnum, ptr, (colnr_T)0, false);
xfree(ptr);
- ptr = vim_strnsave(get_cursor_line_ptr(), curwin->w_cursor.col);
- ml_replace(curwin->w_cursor.lnum, ptr, FALSE);
- ++nr_lines;
+ oldp = get_cursor_line_ptr();
+ p = oldp + curwin->w_cursor.col;
+ if (dir == FORWARD && *p != NUL) {
+ mb_ptr_adv(p);
+ }
+ ptr = vim_strnsave(oldp, p - oldp);
+ ml_replace(curwin->w_cursor.lnum, ptr, false);
+ nr_lines++;
dir = FORWARD;
}
if (flags & PUT_LINE_FORWARD) {
@@ -2710,8 +2750,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
curbuf->b_op_end = curwin->w_cursor; /* default for '] mark */
}
- if (flags & PUT_LINE) /* :put command or "p" in Visual line mode. */
- y_type = MLINE;
+ if (flags & PUT_LINE) { // :put command or "p" in Visual line mode.
+ y_type = kMTLineWise;
+ }
if (y_size == 0 || y_array == NULL) {
EMSG2(_("E353: Nothing in register %s"),
@@ -2719,13 +2760,13 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
goto end;
}
- if (y_type == MBLOCK) {
+ if (y_type == kMTBlockWise) {
lnum = curwin->w_cursor.lnum + y_size + 1;
if (lnum > curbuf->b_ml.ml_line_count)
lnum = curbuf->b_ml.ml_line_count + 1;
if (u_save(curwin->w_cursor.lnum - 1, lnum) == FAIL)
goto end;
- } else if (y_type == MLINE) {
+ } else if (y_type == kMTLineWise) {
lnum = curwin->w_cursor.lnum;
/* Correct line number for closed fold. Don't move the cursor yet,
* u_save() uses it. */
@@ -2749,7 +2790,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
yanklen = (int)STRLEN(y_array[0]);
- if (ve_flags == VE_ALL && y_type == MCHAR) {
+ if (ve_flags == VE_ALL && y_type == kMTCharWise) {
if (gchar_cursor() == TAB) {
/* Don't need to insert spaces when "p" on the last position of a
* tab or "P" on the first position. */
@@ -2769,7 +2810,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
/*
* Block mode
*/
- if (y_type == MBLOCK) {
+ if (y_type == kMTBlockWise) {
char c = gchar_cursor();
colnr_T endcol2 = 0;
@@ -2917,12 +2958,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
} else
curwin->w_cursor.lnum = lnum;
} else {
- /*
- * Character or Line mode
- */
- if (y_type == MCHAR) {
- /* if type is MCHAR, FORWARD is the same as BACKWARD on the next
- * char */
+ // Character or Line mode
+ if (y_type == kMTCharWise) {
+ // if type is kMTCharWise, FORWARD is the same as BACKWARD on the next
+ // char
if (dir == FORWARD && gchar_cursor() != NUL) {
if (has_mbyte) {
int bytelen = (*mb_ptr2len)(get_cursor_pos_ptr());
@@ -2953,7 +2992,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
/*
* simple case: insert into current line
*/
- if (y_type == MCHAR && y_size == 1) {
+ if (y_type == kMTCharWise && y_size == 1) {
do {
totlen = count * yanklen;
if (totlen > 0) {
@@ -2988,18 +3027,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
++curwin->w_cursor.col;
changed_bytes(lnum, col);
} else {
- /*
- * Insert at least one line. When y_type is MCHAR, break the first
- * line in two.
- */
- for (cnt = 1; cnt <= count; ++cnt) {
+ // Insert at least one line. When y_type is kMTCharWise, break the first
+ // line in two.
+ for (cnt = 1; cnt <= count; cnt++) {
i = 0;
- if (y_type == MCHAR) {
- /*
- * Split the current line in two at the insert position.
- * First insert y_array[size - 1] in front of second line.
- * Then append y_array[0] to first line.
- */
+ if (y_type == kMTCharWise) {
+ // Split the current line in two at the insert position.
+ // First insert y_array[size - 1] in front of second line.
+ // Then append y_array[0] to first line.
lnum = new_cursor.lnum;
ptr = ml_get(lnum) + col;
totlen = (int)STRLEN(y_array[y_size - 1]);
@@ -3023,10 +3058,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
for (; i < y_size; i++) {
- if ((y_type != MCHAR || i < y_size - 1)
- && ml_append(lnum, y_array[i], (colnr_T)0, FALSE)
- == FAIL)
+ if ((y_type != kMTCharWise || i < y_size - 1)
+ && ml_append(lnum, y_array[i], (colnr_T)0, false)
+ == FAIL) {
goto error;
+ }
lnum++;
++nr_lines;
if (flags & PUT_FIXINDENT) {
@@ -3055,22 +3091,23 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
error:
- /* Adjust marks. */
- if (y_type == MLINE) {
+ // Adjust marks.
+ if (y_type == kMTLineWise) {
curbuf->b_op_start.col = 0;
if (dir == FORWARD)
curbuf->b_op_start.lnum++;
}
- mark_adjust(curbuf->b_op_start.lnum + (y_type == MCHAR),
- (linenr_T)MAXLNUM, nr_lines, 0L);
+ mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise),
+ (linenr_T)MAXLNUM, nr_lines, 0L);
- /* note changed text for displaying and folding */
- if (y_type == MCHAR)
+ // note changed text for displaying and folding
+ if (y_type == kMTCharWise) {
changed_lines(curwin->w_cursor.lnum, col,
- curwin->w_cursor.lnum + 1, nr_lines);
- else
+ curwin->w_cursor.lnum + 1, nr_lines);
+ } else {
changed_lines(curbuf->b_op_start.lnum, 0,
- curbuf->b_op_start.lnum, nr_lines);
+ curbuf->b_op_start.lnum, nr_lines);
+ }
/* put '] mark at last inserted character */
curbuf->b_op_end.lnum = lnum;
@@ -3086,19 +3123,20 @@ error:
curwin->w_cursor.lnum = lnum;
beginline(BL_WHITE | BL_FIX);
} else if (flags & PUT_CURSEND) {
- /* put cursor after inserted text */
- if (y_type == MLINE) {
- if (lnum >= curbuf->b_ml.ml_line_count)
+ // put cursor after inserted text
+ if (y_type == kMTLineWise) {
+ if (lnum >= curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- else
+ } else {
curwin->w_cursor.lnum = lnum + 1;
+ }
curwin->w_cursor.col = 0;
} else {
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = col;
}
- } else if (y_type == MLINE) {
- /* put cursor on first non-blank in first inserted line */
+ } else if (y_type == kMTLineWise) {
+ // put cursor on first non-blank in first inserted line
curwin->w_cursor.col = 0;
if (dir == FORWARD)
++curwin->w_cursor.lnum;
@@ -3151,11 +3189,9 @@ void adjust_cursor_eol(void)
*/
int preprocs_left(void)
{
- return
- (curbuf->b_p_si && !curbuf->b_p_cin) ||
- (curbuf->b_p_cin && in_cinkeys('#', ' ', TRUE)
- && curbuf->b_ind_hash_comment == 0)
- ;
+ return ((curbuf->b_p_si && !curbuf->b_p_cin)
+ || (curbuf->b_p_cin && in_cinkeys('#', ' ', true)
+ && curbuf->b_ind_hash_comment == 0));
}
/* Return the character name of the register with the given number */
@@ -3237,9 +3273,10 @@ void ex_display(exarg_T *eap)
p += clen - 1;
}
}
- if (n > 1 && yb->y_type == MLINE)
+ if (n > 1 && yb->y_type == kMTLineWise) {
MSG_PUTS_ATTR("^J", attr);
- ui_flush(); /* show one line at a time */
+ }
+ ui_flush(); // show one line at a time
}
os_breakcheck();
}
@@ -3953,7 +3990,7 @@ format_lines (
if (line_count < 0 && u_save_cursor() == FAIL)
break;
if (next_leader_len > 0) {
- (void)del_bytes((long)next_leader_len, FALSE, FALSE);
+ (void)del_bytes(next_leader_len, false, false);
mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
(long)-next_leader_len);
} else if (second_indent > 0) { /* the "leader" for FO_Q_SECOND */
@@ -4229,18 +4266,18 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
}
pos = oap->start;
- for (; pos.lnum <= oap->end.lnum; ++pos.lnum) {
- if (oap->motion_type == MBLOCK) {
+ for (; pos.lnum <= oap->end.lnum; pos.lnum++) {
+ if (oap->motion_type == kMTBlockWise) {
// Visual block mode
block_prep(oap, &bd, pos.lnum, false);
pos.col = bd.textcol;
length = bd.textlen;
- } else if (oap->motion_type == MLINE) {
+ } else if (oap->motion_type == kMTLineWise) {
curwin->w_cursor.col = 0;
pos.col = 0;
length = (colnr_T)STRLEN(ml_get(pos.lnum));
} else {
- // oap->motion_type == MCHAR
+ // oap->motion_type == kMTCharWise
if (!oap->inclusive) {
dec(&(oap->end));
}
@@ -4360,8 +4397,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
if (dobin
&& dohex
&& !((col > 0
- && (ptr[col] == 'X' ||
- ptr[col] == 'x')
+ && (ptr[col] == 'X' || ptr[col] == 'x')
&& ptr[col - 1] == '0'
&& ascii_isxdigit(ptr[col + 1])))) {
// In case of binary/hexadecimal pattern overlap match, rescan
@@ -4375,17 +4411,15 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
if ((dohex
&& col > 0
- && (ptr[col] == 'X'
- || ptr[col] == 'x')
+ && (ptr[col] == 'X' || ptr[col] == 'x')
&& ptr[col - 1] == '0'
- && ascii_isxdigit(ptr[col + 1])) ||
- (dobin
- && col > 0
- && (ptr[col] == 'B'
- || ptr[col] == 'b')
- && ptr[col - 1] == '0'
- && ascii_isbdigit(ptr[col + 1]))) {
- // Found hexadecimal or binary number, move to its start.
+ && ascii_isxdigit(ptr[col + 1]))
+ || (dobin
+ && col > 0
+ && (ptr[col] == 'B' || ptr[col] == 'b')
+ && ptr[col - 1] == '0'
+ && ascii_isbdigit(ptr[col + 1]))) {
+ // Found hexadecimal or binary number, move to its start.
col--;
} else {
// Search forward and then backward to find the start of number.
@@ -4406,8 +4440,8 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
}
if (visual) {
- while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col]) &&
- !(doalp && ASCII_ISALPHA(ptr[col]))) {
+ while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col])
+ && !(doalp && ASCII_ISALPHA(ptr[col]))) {
col++;
length--;
}
@@ -4569,8 +4603,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
*ptr++ = '0';
length--;
}
- if (pre == 'b' || pre == 'B' ||
- pre == 'x' || pre == 'X') {
+ if (pre == 'b' || pre == 'B' || pre == 'x' || pre == 'X') {
*ptr++ = pre;
length--;
}
@@ -4643,38 +4676,71 @@ theend:
/*
* Return the type of a register.
* Used for getregtype()
- * Returns MAUTO for error.
+ * Returns kMTUnknown for error.
*/
-char_u get_reg_type(int regname, long *reglen)
+MotionType get_reg_type(int regname, colnr_T *reg_width)
{
switch (regname) {
- case '%': /* file name */
- case '#': /* alternate file name */
- case '=': /* expression */
- case ':': /* last command line */
- case '/': /* last search-pattern */
- case '.': /* last inserted text */
- case Ctrl_F: /* Filename under cursor */
- case Ctrl_P: /* Path under cursor, expand via "path" */
- case Ctrl_W: /* word under cursor */
- case Ctrl_A: /* WORD (mnemonic All) under cursor */
- case '_': /* black hole: always empty */
- return MCHAR;
+ case '%': // file name
+ case '#': // alternate file name
+ case '=': // expression
+ case ':': // last command line
+ case '/': // last search-pattern
+ case '.': // last inserted text
+ case Ctrl_F: // Filename under cursor
+ case Ctrl_P: // Path under cursor, expand via "path"
+ case Ctrl_W: // word under cursor
+ case Ctrl_A: // WORD (mnemonic All) under cursor
+ case '_': // black hole: always empty
+ return kMTCharWise;
}
- if (regname != NUL && !valid_yank_reg(regname, false))
- return MAUTO;
+ if (regname != NUL && !valid_yank_reg(regname, false)) {
+ return kMTUnknown;
+ }
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
if (reg->y_array != NULL) {
- if (reglen != NULL && reg->y_type == MBLOCK)
- *reglen = reg->y_width;
+ if (reg_width != NULL && reg->y_type == kMTBlockWise) {
+ *reg_width = reg->y_width;
+ }
return reg->y_type;
}
- return MAUTO;
+ return kMTUnknown;
+}
+
+/// Format the register type as a string.
+///
+/// @param reg_type The register type.
+/// @param reg_width The width, only used if "reg_type" is kMTBlockWise.
+/// @param[out] buf Buffer to store formatted string. The allocated size should
+/// be at least NUMBUFLEN+2 to always fit the value.
+/// @param buf_len The allocated size of the buffer.
+void format_reg_type(MotionType reg_type, colnr_T reg_width,
+ char *buf, size_t buf_len)
+ FUNC_ATTR_NONNULL_ALL
+{
+ assert(buf_len > 1);
+ switch (reg_type) {
+ case kMTLineWise:
+ buf[0] = 'V';
+ buf[1] = NUL;
+ break;
+ case kMTCharWise:
+ buf[0] = 'v';
+ buf[1] = NUL;
+ break;
+ case kMTBlockWise:
+ snprintf(buf, buf_len, CTRL_V_STR "%" PRIdCOLNR, reg_width + 1);
+ break;
+ case kMTUnknown:
+ buf[0] = NUL;
+ break;
+ }
}
+
/// When `flags` has `kGRegList` return a list with text `s`.
/// Otherwise just return `s`.
///
@@ -4753,10 +4819,11 @@ void *get_reg_contents(int regname, int flags)
len += STRLEN(reg->y_array[i]);
/*
* Insert a newline between lines and after last line if
- * y_type is MLINE.
+ * y_type is kMTLineWise.
*/
- if (reg->y_type == MLINE || i < reg->y_size - 1)
- ++len;
+ if (reg->y_type == kMTLineWise || i < reg->y_size - 1) {
+ len++;
+ }
}
retval = xmalloc(len + 1);
@@ -4771,10 +4838,11 @@ void *get_reg_contents(int regname, int flags)
/*
* Insert a NL between lines and after the last line if y_type is
- * MLINE.
+ * kMTLineWise.
*/
- if (reg->y_type == MLINE || i < reg->y_size - 1)
+ if (reg->y_type == kMTLineWise || i < reg->y_size - 1) {
retval[len++] = '\n';
+ }
}
retval[len] = NUL;
@@ -4815,11 +4883,12 @@ static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous
void write_reg_contents(int name, const char_u *str, ssize_t len,
int must_append)
{
- write_reg_contents_ex(name, str, len, must_append, MAUTO, 0L);
+ write_reg_contents_ex(name, str, len, must_append, kMTUnknown, 0L);
}
void write_reg_contents_lst(int name, char_u **strings, int maxlen,
- bool must_append, int yank_type, long block_len)
+ bool must_append, MotionType yank_type,
+ long block_len)
{
if (name == '/' || name == '=') {
char_u *s = strings[0];
@@ -4865,13 +4934,13 @@ void write_reg_contents_lst(int name, char_u **strings, int maxlen,
/// contents of the register. Note that regardless of
/// `must_append`, this function will append when `name`
/// is an uppercase letter.
-/// @param yank_type MCHAR, MLINE, MBLOCK or MAUTO
+/// @param yank_type The motion type (kMTUnknown to auto detect)
/// @param block_len width of visual block
void write_reg_contents_ex(int name,
const char_u *str,
ssize_t len,
bool must_append,
- int yank_type,
+ MotionType yank_type,
long block_len)
{
if (len < 0) {
@@ -4945,24 +5014,24 @@ void write_reg_contents_ex(int name,
/// When the register is not empty, the string is appended.
///
/// @param y_ptr pointer to yank register
-/// @param yank_type MCHAR, MLINE, MBLOCK or MAUTO
+/// @param yank_type The motion type (kMTUnknown to auto detect)
/// @param str string or list of strings to put in register
/// @param len length of the string (Ignored when str_list=true.)
/// @param blocklen width of visual block, or -1 for "I don't know."
/// @param str_list True if str is `char_u **`.
-static void str_to_reg(yankreg_T *y_ptr, int yank_type, const char_u *str,
- size_t len, colnr_T blocklen, bool str_list)
+static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type,
+ const char_u *str, size_t len, colnr_T blocklen,
+ bool str_list)
FUNC_ATTR_NONNULL_ALL
{
if (y_ptr->y_array == NULL) { // NULL means empty register
y_ptr->y_size = 0;
}
- int type = yank_type; // MCHAR, MLINE or MBLOCK
- if (yank_type == MAUTO) {
- type = ((str_list ||
- (len > 0 && (str[len - 1] == NL || str[len - 1] == CAR)))
- ? MLINE : MCHAR);
+ if (yank_type == kMTUnknown) {
+ yank_type = ((str_list
+ || (len > 0 && (str[len - 1] == NL || str[len - 1] == CAR)))
+ ? kMTLineWise : kMTCharWise);
}
size_t newlines = 0;
@@ -4976,11 +5045,11 @@ static void str_to_reg(yankreg_T *y_ptr, int yank_type, const char_u *str,
}
} else {
newlines = memcnt(str, '\n', len);
- if (type == MCHAR || len == 0 || str[len - 1] != '\n') {
+ if (yank_type == kMTCharWise || len == 0 || str[len - 1] != '\n') {
extraline = 1;
++newlines; // count extra newline at the end
}
- if (y_ptr->y_size > 0 && y_ptr->y_type == MCHAR) {
+ if (y_ptr->y_size > 0 && y_ptr->y_type == kMTCharWise) {
append = true;
--newlines; // uncount newline when appending first line
}
@@ -5033,11 +5102,11 @@ static void str_to_reg(yankreg_T *y_ptr, int yank_type, const char_u *str,
memchrsub(pp[lnum], NUL, '\n', s_len);
}
}
- y_ptr->y_type = type;
+ y_ptr->y_type = yank_type;
y_ptr->y_size = lnum;
set_yreg_additional_data(y_ptr, NULL);
y_ptr->timestamp = os_time();
- if (type == MBLOCK) {
+ if (yank_type == kMTBlockWise) {
y_ptr->y_width = (blocklen == -1 ? (colnr_T) maxlen - 1 : blocklen);
} else {
y_ptr->y_width = 0;
@@ -5096,18 +5165,18 @@ static long line_count_info(char_u *line, long *wc, long *cc, long limit, int eo
return i;
}
-/*
- * Give some info about the position of the cursor (for "g CTRL-G").
- * In Visual mode, give some info about the selected region. (In this case,
- * the *_count_cursor variables store running totals for the selection.)
- */
-void cursor_pos_info(void)
+/// Give some info about the position of the cursor (for "g CTRL-G").
+/// In Visual mode, give some info about the selected region. (In this case,
+/// the *_count_cursor variables store running totals for the selection.)
+/// When "dict" is not NULL store the info there instead of showing it.
+void cursor_pos_info(dict_T *dict)
{
char_u *p;
char_u buf1[50];
char_u buf2[40];
linenr_T lnum;
long byte_count = 0;
+ long bom_count = 0;
long byte_count_cursor = 0;
long char_count = 0;
long char_count_cursor = 0;
@@ -5122,11 +5191,12 @@ void cursor_pos_info(void)
const int l_VIsual_active = VIsual_active;
const int l_VIsual_mode = VIsual_mode;
- /*
- * Compute the length of the file in characters.
- */
+ // Compute the length of the file in characters.
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
- MSG(_(no_lines_msg));
+ if (dict == NULL) {
+ MSG(_(no_lines_msg));
+ return;
+ }
} else {
if (get_fileformat(curbuf) == EOL_DOS)
eol_size = 2;
@@ -5150,7 +5220,7 @@ void cursor_pos_info(void)
/* Make 'sbr' empty for a moment to get the correct size. */
p_sbr = empty_option;
oparg.is_VIsual = true;
- oparg.motion_type = MBLOCK;
+ oparg.motion_type = kMTBlockWise;
oparg.op_type = OP_NOP;
getvcols(curwin, &min_pos, &max_pos,
&oparg.start_vcol, &oparg.end_vcol);
@@ -5231,79 +5301,103 @@ void cursor_pos_info(void)
&char_count, (long)MAXCOL, eol_size);
}
- /* Correction for when last line doesn't have an EOL. */
- if (!curbuf->b_p_eol && (curbuf->b_p_bin || !curbuf->b_p_fixeol))
+ // Correction for when last line doesn't have an EOL.
+ if (!curbuf->b_p_eol && (curbuf->b_p_bin || !curbuf->b_p_fixeol)) {
byte_count -= eol_size;
+ }
- if (l_VIsual_active) {
- if (l_VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL) {
- getvcols(curwin, &min_pos, &max_pos, &min_pos.col,
- &max_pos.col);
- vim_snprintf((char *)buf1, sizeof(buf1), _("%" PRId64 " Cols; "),
- (int64_t)(oparg.end_vcol - oparg.start_vcol + 1));
- } else
- buf1[0] = NUL;
-
- if (char_count_cursor == byte_count_cursor
- && char_count == byte_count)
- vim_snprintf((char *)IObuff, IOSIZE,
- _("Selected %s%" PRId64 " of %" PRId64 " Lines; %" PRId64
- " of %" PRId64 " Words; %" PRId64 " of %" PRId64 " Bytes"),
- buf1, (int64_t)line_count_selected,
- (int64_t)curbuf->b_ml.ml_line_count,
- (int64_t)word_count_cursor, (int64_t)word_count,
- (int64_t)byte_count_cursor, (int64_t)byte_count);
- else
- vim_snprintf((char *)IObuff, IOSIZE,
- _("Selected %s%" PRId64 " of %" PRId64 " Lines; %" PRId64
- " of %" PRId64 " Words; %" PRId64 " of %" PRId64
- " Chars; %" PRId64 " of %" PRId64 " Bytes"),
- buf1, (int64_t)line_count_selected,
- (int64_t)curbuf->b_ml.ml_line_count,
- (int64_t)word_count_cursor, (int64_t)word_count,
- (int64_t)char_count_cursor, (int64_t)char_count,
- (int64_t)byte_count_cursor, (int64_t)byte_count);
- } else {
- p = get_cursor_line_ptr();
- validate_virtcol();
- col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
- (int)curwin->w_virtcol + 1);
- col_print(buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p));
-
- if (char_count_cursor == byte_count_cursor
- && char_count == byte_count)
- vim_snprintf((char *)IObuff, IOSIZE,
- _("Col %s of %s; Line %" PRId64 " of %" PRId64 "; Word %" PRId64
- " of %" PRId64 "; Byte %" PRId64 " of %" PRId64 ""),
- (char *)buf1, (char *)buf2,
- (int64_t)curwin->w_cursor.lnum,
- (int64_t)curbuf->b_ml.ml_line_count,
- (int64_t)word_count_cursor, (int64_t)word_count,
- (int64_t)byte_count_cursor, (int64_t)byte_count);
- else
- vim_snprintf((char *)IObuff, IOSIZE,
- _(
- "Col %s of %s; Line %" PRId64 " of %" PRId64 "; Word %" PRId64
- " of %" PRId64 "; Char %" PRId64 " of %" PRId64
- "; Byte %" PRId64 " of %" PRId64 ""),
- (char *)buf1, (char *)buf2,
- (int64_t)curwin->w_cursor.lnum,
- (int64_t)curbuf->b_ml.ml_line_count,
- (int64_t)word_count_cursor, (int64_t)word_count,
- (int64_t)char_count_cursor, (int64_t)char_count,
- (int64_t)byte_count_cursor, (int64_t)byte_count);
- }
-
- byte_count = bomb_size();
- if (byte_count > 0)
- sprintf((char *)IObuff + STRLEN(IObuff), _("(+%" PRId64 " for BOM)"),
- (int64_t)byte_count);
- /* Don't shorten this message, the user asked for it. */
- p = p_shm;
- p_shm = (char_u *)"";
- msg(IObuff);
- p_shm = p;
+ if (dict == NULL) {
+ if (l_VIsual_active) {
+ if (l_VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL) {
+ getvcols(curwin, &min_pos, &max_pos, &min_pos.col, &max_pos.col);
+ vim_snprintf((char *)buf1, sizeof(buf1), _("%" PRId64 " Cols; "),
+ (int64_t)(oparg.end_vcol - oparg.start_vcol + 1));
+ } else {
+ buf1[0] = NUL;
+ }
+
+ if (char_count_cursor == byte_count_cursor
+ && char_count == byte_count) {
+ vim_snprintf((char *)IObuff, IOSIZE,
+ _("Selected %s%" PRId64 " of %" PRId64 " Lines;"
+ " %" PRId64 " of %" PRId64 " Words;"
+ " %" PRId64 " of %" PRId64 " Bytes"),
+ buf1, (int64_t)line_count_selected,
+ (int64_t)curbuf->b_ml.ml_line_count,
+ (int64_t)word_count_cursor, (int64_t)word_count,
+ (int64_t)byte_count_cursor, (int64_t)byte_count);
+ } else {
+ vim_snprintf((char *)IObuff, IOSIZE,
+ _("Selected %s%" PRId64 " of %" PRId64 " Lines;"
+ " %" PRId64 " of %" PRId64 " Words;"
+ " %" PRId64 " of %" PRId64 " Chars;"
+ " %" PRId64 " of %" PRId64 " Bytes"),
+ buf1, (int64_t)line_count_selected,
+ (int64_t)curbuf->b_ml.ml_line_count,
+ (int64_t)word_count_cursor, (int64_t)word_count,
+ (int64_t)char_count_cursor, (int64_t)char_count,
+ (int64_t)byte_count_cursor, (int64_t)byte_count);
+ }
+ } else {
+ p = get_cursor_line_ptr();
+ validate_virtcol();
+ col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
+ (int)curwin->w_virtcol + 1);
+ col_print(buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p));
+
+ if (char_count_cursor == byte_count_cursor
+ && char_count == byte_count) {
+ vim_snprintf((char *)IObuff, IOSIZE,
+ _("Col %s of %s; Line %" PRId64 " of %" PRId64 ";"
+ " Word %" PRId64 " of %" PRId64 ";"
+ " Byte %" PRId64 " of %" PRId64 ""),
+ (char *)buf1, (char *)buf2,
+ (int64_t)curwin->w_cursor.lnum,
+ (int64_t)curbuf->b_ml.ml_line_count,
+ (int64_t)word_count_cursor, (int64_t)word_count,
+ (int64_t)byte_count_cursor, (int64_t)byte_count);
+ } else {
+ vim_snprintf((char *)IObuff, IOSIZE,
+ _("Col %s of %s; Line %" PRId64 " of %" PRId64 ";"
+ " Word %" PRId64 " of %" PRId64 ";"
+ " Char %" PRId64 " of %" PRId64 ";"
+ " Byte %" PRId64 " of %" PRId64 ""),
+ (char *)buf1, (char *)buf2,
+ (int64_t)curwin->w_cursor.lnum,
+ (int64_t)curbuf->b_ml.ml_line_count,
+ (int64_t)word_count_cursor, (int64_t)word_count,
+ (int64_t)char_count_cursor, (int64_t)char_count,
+ (int64_t)byte_count_cursor, (int64_t)byte_count);
+ }
+ }
+ }
+
+ bom_count = bomb_size();
+ if (bom_count > 0) {
+ vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
+ _("(+%" PRId64 " for BOM)"), (int64_t)bom_count);
+ }
+ if (dict == NULL) {
+ p = p_shm;
+ p_shm = (char_u *)"";
+ msg(IObuff);
+ p_shm = p;
+ }
}
+
+ if (dict != NULL) {
+ // Don't shorten this message, the user asked for it.
+ dict_add_nr_str(dict, "words", word_count, NULL);
+ dict_add_nr_str(dict, "chars", char_count, NULL);
+ dict_add_nr_str(dict, "bytes", byte_count + bom_count, NULL);
+
+ dict_add_nr_str(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes",
+ byte_count_cursor, NULL);
+ dict_add_nr_str(dict, l_VIsual_active ? "visual_chars" : "cursor_chars",
+ char_count_cursor, NULL);
+ dict_add_nr_str(dict, l_VIsual_active ? "visual_words" : "cursor_words",
+ word_count_cursor, NULL);
+ }
}
/// Check if the default register (used in an unnamed paste) should be a
@@ -5405,16 +5499,16 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
}
switch (regtype[0]) {
case 0:
- reg->y_type = MAUTO;
+ reg->y_type = kMTUnknown;
break;
case 'v': case 'c':
- reg->y_type = MCHAR;
+ reg->y_type = kMTCharWise;
break;
case 'V': case 'l':
- reg->y_type = MLINE;
+ reg->y_type = kMTLineWise;
break;
case 'b': case Ctrl_V:
- reg->y_type = MBLOCK;
+ reg->y_type = kMTBlockWise;
break;
default:
goto err;
@@ -5422,7 +5516,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
} else {
lines = res;
// provider did not specify regtype, calculate it below
- reg->y_type = MAUTO;
+ reg->y_type = kMTUnknown;
}
reg->y_array = xcalloc(lines->lv_len, sizeof(uint8_t *));
@@ -5443,20 +5537,20 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) {
// a known-to-be charwise yank might have a final linebreak
// but otherwise there is no line after the final newline
- if (reg->y_type != MCHAR) {
+ if (reg->y_type != kMTCharWise) {
xfree(reg->y_array[reg->y_size-1]);
reg->y_size--;
- if (reg->y_type == MAUTO) {
- reg->y_type = MLINE;
+ if (reg->y_type == kMTUnknown) {
+ reg->y_type = kMTLineWise;
}
}
} else {
- if (reg->y_type == MAUTO) {
- reg->y_type = MCHAR;
+ if (reg->y_type == kMTUnknown) {
+ reg->y_type = kMTCharWise;
}
}
- if (reg->y_type == MBLOCK) {
+ if (reg->y_type == kMTBlockWise) {
int maxlen = 0;
for (int i = 0; i < reg->y_size; i++) {
int rowlen = STRLEN(reg->y_array[i]);
@@ -5505,17 +5599,19 @@ static void set_clipboard(int name, yankreg_T *reg)
char_u regtype;
switch (reg->y_type) {
- case MLINE:
+ case kMTLineWise:
regtype = 'V';
list_append_string(lines, (char_u*)"", 0);
break;
- case MCHAR:
+ case kMTCharWise:
regtype = 'v';
break;
- case MBLOCK:
+ case kMTBlockWise:
regtype = 'b';
list_append_string(lines, (char_u*)"", 0);
break;
+ case kMTUnknown:
+ assert(false);
}
list_append_string(args, &regtype, 1);
@@ -5556,7 +5652,7 @@ static inline bool reg_empty(const yankreg_T *const reg)
return (reg->y_array == NULL
|| reg->y_size == 0
|| (reg->y_size == 1
- && reg->y_type == MCHAR
+ && reg->y_type == kMTCharWise
&& *(reg->y_array[0]) == NUL));
}
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
index f33e87572f..8c8a586957 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -78,10 +78,10 @@ enum GRegFlags {
/// Definition of one register
typedef struct yankreg {
- char_u **y_array; ///< Pointer to an array of line pointers.
- linenr_T y_size; ///< Number of lines in y_array.
- char_u y_type; ///< Register type: MLINE, MCHAR or MBLOCK.
- colnr_T y_width; ///< Register width (only valid for y_type == MBLOCK).
+ char_u **y_array; ///< Pointer to an array of line pointers.
+ linenr_T y_size; ///< Number of lines in y_array.
+ MotionType y_type; ///< Register type
+ colnr_T y_width; ///< Register width (only valid for y_type == kBlockWise).
Timestamp timestamp; ///< Time when register was last modified.
dict_T *additional_data; ///< Additional data from ShaDa file.
} yankreg_T;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index d3a2ce971d..45ebb4fa4c 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -32,6 +32,7 @@
#include "nvim/vim.h"
#include "nvim/ascii.h"
+#include "nvim/edit.h"
#include "nvim/option.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
@@ -168,11 +169,12 @@ static int p_ml_nobin;
static long p_tw_nobin;
static long p_wm_nobin;
-/* Saved values for when 'paste' is set */
+// Saved values for when 'paste' is set.
+static int p_ai_nopaste;
+static int p_et_nopaste;
+static long p_sts_nopaste;
static long p_tw_nopaste;
static long p_wm_nopaste;
-static long p_sts_nopaste;
-static int p_ai_nopaste;
typedef struct vimoption {
char *fullname; /* full option name */
@@ -218,20 +220,22 @@ typedef struct vimoption {
#define P_RALL 0x6000U /* redraw all windows */
#define P_RCLR 0x7000U /* clear and redraw all */
-#define P_COMMA 0x8000U /* comma separated list */
-#define P_NODUP 0x10000U /* don't allow duplicate strings */
-#define P_FLAGLIST 0x20000U /* list of single-char flags */
-
-#define P_SECURE 0x40000U /* cannot change in modeline or secure mode */
-#define P_GETTEXT 0x80000U /* expand default value with _() */
-#define P_NOGLOB 0x100000U /* do not use local value for global vimrc */
-#define P_NFNAME 0x200000U /* only normal file name chars allowed */
-#define P_INSECURE 0x400000U /* option was set from a modeline */
-#define P_PRI_MKRC 0x800000U /* priority for :mkvimrc (setting option has
- side effects) */
-#define P_NO_ML 0x1000000U /* not allowed in modeline */
-#define P_CURSWANT 0x2000000U /* update curswant required; not needed when
- * there is a redraw flag */
+#define P_COMMA 0x8000U ///< comma separated list
+#define P_ONECOMMA 0x18000U ///< P_COMMA and cannot have two consecutive
+ ///< commas
+#define P_NODUP 0x20000U ///< don't allow duplicate strings
+#define P_FLAGLIST 0x40000U ///< list of single-char flags
+
+#define P_SECURE 0x80000U ///< cannot change in modeline or secure mode
+#define P_GETTEXT 0x100000U ///< expand default value with _()
+#define P_NOGLOB 0x200000U ///< do not use local value for global vimrc
+#define P_NFNAME 0x400000U ///< only normal file name chars allowed
+#define P_INSECURE 0x800000U ///< option was set from a modeline
+#define P_PRI_MKRC 0x1000000U ///< priority for :mkvimrc (setting option
+ ///< has side effects)
+#define P_NO_ML 0x2000000U ///< not allowed in modeline
+#define P_CURSWANT 0x4000000U ///< update curswant required; not needed
+ ///< when there is a redraw flag
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \
@@ -708,15 +712,11 @@ void set_init_1(void)
/* Must be before option_expand(), because that one needs vim_isIDc() */
didset_options();
- /* Use the current chartab for the generic chartab. */
+ // Use the current chartab for the generic chartab. This is not in
+ // didset_options() because it only depends on 'encoding'.
init_spell_chartab();
/*
- * initialize the table for 'breakat'.
- */
- fill_breakat_flags();
-
- /*
* Expand environment variables and things like "~" for the defaults.
* If option_expand() returns non-NULL the variable is expanded. This can
* only happen for non-indirect options.
@@ -748,14 +748,8 @@ void set_init_1(void)
}
}
- /* Initialize the highlight_attr[] table. */
- highlight_changed();
-
save_file_ff(curbuf); /* Buffer is unchanged */
- /* Parse default for 'wildmode' */
- check_opt_wim();
-
/* Detect use of mlterm.
* Mlterm is a terminal emulator akin to xterm that has some special
* abilities (bidi namely).
@@ -765,11 +759,7 @@ void set_init_1(void)
if (os_env_exists("MLTERM"))
set_option_value((char_u *)"tbidi", 1L, NULL, 0);
- /* Parse default for 'fillchars'. */
- (void)set_chars_option(&p_fcs);
-
- /* Parse default for 'listchars'. */
- (void)set_chars_option(&p_lcs);
+ didset_options2();
// enc_locale() will try to find the encoding of the current locale.
// This will be used when 'default' is used as encoding specifier
@@ -1148,9 +1138,12 @@ do_set (
*/
arg += 3;
if (*arg == '&') {
- ++arg;
- /* Only for :set command set global value of local options. */
+ arg++;
+ // Only for :set command set global value of local options.
set_options_default(OPT_FREE | opt_flags);
+ didset_options();
+ didset_options2();
+ redraw_all_later(CLEAR);
} else {
showoptions(1, opt_flags);
did_show = TRUE;
@@ -1185,28 +1178,27 @@ do_set (
errmsg = e_invarg;
goto skip;
}
- arg[len] = NUL; /* put NUL after name */
- if (arg[1] == 't' && arg[2] == '_') /* could be term code */
- opt_idx = findoption(arg + 1);
- arg[len++] = '>'; /* restore '>' */
- if (opt_idx == -1)
+ if (arg[1] == 't' && arg[2] == '_') { // could be term code
+ opt_idx = findoption_len(arg + 1, (size_t) (len - 1));
+ }
+ len++;
+ if (opt_idx == -1) {
key = find_key_option(arg + 1);
+ }
} else {
len = 0;
- /*
- * The two characters after "t_" may not be alphanumeric.
- */
- if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3])
+ // The two characters after "t_" may not be alphanumeric.
+ if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) {
len = 4;
- else
- while (ASCII_ISALNUM(arg[len]) || arg[len] == '_')
- ++len;
- nextchar = arg[len];
- arg[len] = NUL; /* put NUL after name */
- opt_idx = findoption(arg);
- arg[len] = nextchar; /* restore nextchar */
- if (opt_idx == -1)
+ } else {
+ while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') {
+ len++;
+ }
+ }
+ opt_idx = findoption_len(arg, (size_t) len);
+ if (opt_idx == -1) {
key = find_key_option(arg);
+ }
}
/* remember character after option name */
@@ -1456,7 +1448,7 @@ do_set (
char_u *oldval = NULL; // previous value if *varp
char_u *newval;
char_u *origval = NULL;
- char_u *saved_origval = NULL;
+ char *saved_origval = NULL;
unsigned newlen;
int comma;
int bs;
@@ -1647,18 +1639,21 @@ do_set (
&& STRNCMP(s, newval, i) == 0
&& (!(flags & P_COMMA)
|| s[i] == ','
- || s[i] == NUL))
+ || s[i] == NUL)) {
break;
- /* Count backslashes. Only a comma with an
- * even number of backslashes before it is
- * recognized as a separator */
- if (s > origval && s[-1] == '\\')
- ++bs;
- else
+ }
+ // Count backslashes. Only a comma with an even number of
+ // backslashes or a single backslash preceded by a comma
+ // before it is recognized as a separator
+ if ((s > origval + 1 && s[-1] == '\\' && s[-2] != ',')
+ || (s == origval + 1 && s[-1] == '\\')) {
+ bs++;
+ } else {
bs = 0;
+ }
}
- /* do not add if already there */
+ // do not add if already there
if ((adding || prepending) && *s) {
prepending = FALSE;
adding = FALSE;
@@ -1674,9 +1669,11 @@ do_set (
if (adding) {
i = (int)STRLEN(origval);
// Strip a trailing comma, would get 2.
- if (comma && i > 1 && origval[i - 1] == ','
+ if (comma && i > 1
+ && (flags & P_ONECOMMA) == P_ONECOMMA
+ && origval[i - 1] == ','
&& origval[i - 2] != '\\') {
- --i;
+ i--;
}
memmove(newval + i + comma, newval,
STRLEN(newval) + 1);
@@ -1731,7 +1728,7 @@ do_set (
if (!starting && origval != NULL) {
// origval may be freed by
// did_set_string_option(), make a copy.
- saved_origval = vim_strsave(origval);
+ saved_origval = xstrdup((char *) origval);
}
/* Handle side effects, and set the global value for
@@ -1746,11 +1743,10 @@ do_set (
}
if (saved_origval != NULL) {
- char_u buf_type[7];
- vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s",
+ char buf_type[7];
+ vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
- set_vim_var_string(VV_OPTION_NEW,
- *(char_u **)varp, -1);
+ set_vim_var_string(VV_OPTION_NEW, *(char **) varp, -1);
set_vim_var_string(VV_OPTION_OLD, saved_origval, -1);
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
apply_autocmds(EVENT_OPTIONSET,
@@ -2064,13 +2060,36 @@ static void didset_options(void)
(void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true);
(void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true);
(void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true);
+ (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false);
(void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true);
(void)spell_check_msm();
(void)spell_check_sps();
(void)compile_cap_prog(curwin->w_s);
- /* set cedit_key */
+ (void)did_set_spell_option(true);
+ // set cedit_key
(void)check_cedit();
briopt_check(curwin);
+ // initialize the table for 'breakat'.
+ fill_breakat_flags();
+}
+
+// More side effects of setting options.
+static void didset_options2(void)
+{
+ // Initialize the highlight_attr[] table.
+ (void)highlight_changed();
+
+ // Parse default for 'clipboard'.
+ (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);
+
+ // Parse default for 'fillchars'.
+ (void)set_chars_option(&p_fcs);
+
+ // Parse default for 'listchars'.
+ (void)set_chars_option(&p_lcs);
+
+ // Parse default for 'wildmode'.
+ check_opt_wim();
}
/*
@@ -2129,6 +2148,7 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_ep);
check_string_option(&buf->b_p_path);
check_string_option(&buf->b_p_tags);
+ check_string_option(&buf->b_p_tc);
check_string_option(&buf->b_p_dict);
check_string_option(&buf->b_p_tsr);
check_string_option(&buf->b_p_lw);
@@ -2308,7 +2328,7 @@ set_string_option (
char_u *s;
char_u **varp;
char_u *oldval;
- char_u *saved_oldval = NULL;
+ char *saved_oldval = NULL;
char_u *r = NULL;
if (options[opt_idx].var == NULL) /* don't set hidden option */
@@ -2324,7 +2344,7 @@ set_string_option (
*varp = s;
if (!starting) {
- saved_oldval = vim_strsave(oldval);
+ saved_oldval = xstrdup((char *) oldval);
}
if ((r = did_set_string_option(opt_idx, varp, (int)true, oldval, NULL,
@@ -2333,10 +2353,10 @@ set_string_option (
// call autocommand after handling side effects
if (saved_oldval != NULL) {
- char_u buf_type[7];
- vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s",
+ char buf_type[7];
+ vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
- set_vim_var_string(VV_OPTION_NEW, *varp, -1);
+ set_vim_var_string(VV_OPTION_NEW, (char *) (*varp), -1);
set_vim_var_string(VV_OPTION_OLD, saved_oldval, -1);
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
apply_autocmds(EVENT_OPTIONSET,
@@ -2849,22 +2869,7 @@ did_set_string_option (
|| varp == &(curwin->w_s->b_p_spf)) {
// When 'spelllang' or 'spellfile' is set and there is a window for this
// buffer in which 'spell' is set load the wordlists.
- if (varp == &(curwin->w_s->b_p_spf)) {
- int l = (int)STRLEN(curwin->w_s->b_p_spf);
- if (l > 0
- && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
- errmsg = e_invarg;
- }
- }
-
- if (errmsg == NULL) {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == curbuf && wp->w_p_spell) {
- errmsg = did_set_spelllang(wp);
- break;
- }
- }
- }
+ errmsg = did_set_spell_option(varp == &(curwin->w_s->b_p_spf));
}
/* When 'spellcapcheck' is set compile the regexp program. */
else if (varp == &(curwin->w_s->b_p_spc)) {
@@ -2957,13 +2962,17 @@ did_set_string_option (
}
/* 'completeopt' */
else if (varp == &p_cot) {
- if (check_opt_strings(p_cot, p_cot_values, TRUE) != OK)
+ if (check_opt_strings(p_cot, p_cot_values, true) != OK) {
errmsg = e_invarg;
+ } else {
+ completeopt_was_set();
+ }
}
/* 'pastetoggle': translate key codes like in a mapping */
else if (varp == &p_pt) {
if (*p_pt) {
- (void)replace_termcodes(p_pt, &p, TRUE, TRUE, FALSE);
+ (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, false,
+ CPO_TO_CPO_FLAGS);
if (p != NULL) {
if (new_value_alloced)
free_string_option(p_pt);
@@ -2983,6 +2992,24 @@ did_set_string_option (
if (opt_strings_flags(p_bo, p_bo_values, &bo_flags, true) != OK) {
errmsg = e_invarg;
}
+ } else if (gvarp == &p_tc) { // 'tagcase'
+ unsigned int *flags;
+
+ if (opt_flags & OPT_LOCAL) {
+ p = curbuf->b_p_tc;
+ flags = &curbuf->b_tc_flags;
+ } else {
+ p = p_tc;
+ flags = &tc_flags;
+ }
+
+ if ((opt_flags & OPT_LOCAL) && *p == NUL) {
+ // make the local value empty: use the global value
+ *flags = 0;
+ } else if (*p == NUL
+ || opt_strings_flags(p, p_tc_values, flags, false) != OK) {
+ errmsg = e_invarg;
+ }
} else if (varp == &p_cmp) { // 'casemap'
if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK)
errmsg = e_invarg;
@@ -3420,6 +3447,30 @@ char_u *check_stl_option(char_u *s)
return NULL;
}
+static char_u *did_set_spell_option(bool is_spellfile)
+{
+ char_u *errmsg = NULL;
+
+ if (is_spellfile) {
+ int l = (int)STRLEN(curwin->w_s->b_p_spf);
+ if (l > 0
+ && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
+ errmsg = e_invarg;
+ }
+ }
+
+ if (errmsg == NULL) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == curbuf && wp->w_p_spell) {
+ errmsg = did_set_spelllang(wp);
+ break;
+ }
+ }
+ }
+
+ return errmsg;
+}
+
/*
* Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'.
* Return error message when failed, NULL when OK.
@@ -3775,7 +3826,7 @@ set_bool_option (
msg_source(hl_attr(HLF_W));
MSG_ATTR(_(w_arabic), hl_attr(HLF_W));
- set_vim_var_string(VV_WARNINGMSG, (char_u *)_(w_arabic), -1);
+ set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1);
}
/* set 'delcombine' */
@@ -3822,14 +3873,14 @@ set_bool_option (
options[opt_idx].flags |= P_WAS_SET;
if (!starting) {
- char_u buf_old[2];
- char_u buf_new[2];
- char_u buf_type[7];
- vim_snprintf((char *)buf_old, ARRAY_SIZE(buf_old), "%d",
+ char buf_old[2];
+ char buf_new[2];
+ char buf_type[7];
+ vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d",
old_value ? true: false);
- vim_snprintf((char *)buf_new, ARRAY_SIZE(buf_new), "%d",
+ vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d",
value ? true: false);
- vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s",
+ vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
@@ -4212,12 +4263,12 @@ set_num_option (
options[opt_idx].flags |= P_WAS_SET;
if (!starting && errmsg == NULL) {
- char_u buf_old[NUMBUFLEN];
- char_u buf_new[NUMBUFLEN];
- char_u buf_type[7];
- vim_snprintf((char *)buf_old, ARRAY_SIZE(buf_old), "%ld", old_value);
- vim_snprintf((char *)buf_new, ARRAY_SIZE(buf_new), "%ld", value);
- vim_snprintf((char *)buf_type, ARRAY_SIZE(buf_type), "%s",
+ char buf_old[NUMBUFLEN];
+ char buf_new[NUMBUFLEN];
+ char buf_type[7];
+ vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value);
+ vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value);
+ vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
set_vim_var_string(VV_OPTION_OLD, buf_old, -1);
@@ -4259,14 +4310,16 @@ static void check_redraw(uint32_t flags)
redraw_all_later(NOT_VALID);
}
-/*
- * Find index for option 'arg'.
- * Return -1 if not found.
- */
-static int findoption(char_u *arg)
+/// Find index for named option
+///
+/// @param[in] arg Option to find index for.
+/// @param[in] len Length of the option.
+///
+/// @return Index of the option or -1 if option was not found.
+int findoption_len(const char_u *const arg, const size_t len)
{
- char *s, *p;
- static short quick_tab[27] = {0, 0}; /* quick access table */
+ char *s, *p;
+ static int quick_tab[27] = { 0, 0 }; // quick access table
int is_term_opt;
/*
@@ -4290,25 +4343,31 @@ static int findoption(char_u *arg)
/*
* Check for name starting with an illegal character.
*/
- if (arg[0] < 'a' || arg[0] > 'z')
+ if (len == 0 || arg[0] < 'a' || arg[0] > 'z') {
return -1;
+ }
int opt_idx;
- is_term_opt = (arg[0] == 't' && arg[1] == '_');
- if (is_term_opt)
+ is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_');
+ if (is_term_opt) {
opt_idx = quick_tab[26];
- else
+ } else {
opt_idx = quick_tab[CharOrdLow(arg[0])];
+ }
+ // Match full name
for (; (s = options[opt_idx].fullname) != NULL; opt_idx++) {
- if (STRCMP(arg, s) == 0) /* match full name */
+ if (STRNCMP(arg, s, len) == 0 && s[len] == NUL) {
break;
+ }
}
if (s == NULL && !is_term_opt) {
opt_idx = quick_tab[CharOrdLow(arg[0])];
+ // Match short name
for (; options[opt_idx].fullname != NULL; opt_idx++) {
s = options[opt_idx].shortname;
- if (s != NULL && STRCMP(arg, s) == 0) /* match short name */
+ if (s != NULL && STRNCMP(arg, s, len) == 0 && s[len] == NUL) {
break;
+ }
s = NULL;
}
}
@@ -4376,6 +4435,15 @@ bool set_tty_option(char *name, char *value)
}
/*
+ * Find index for option 'arg'.
+ * Return -1 if not found.
+ */
+static int findoption(char_u *arg)
+{
+ return findoption_len(arg, STRLEN(arg));
+}
+
+/*
* Get the value for an option.
*
* Returns:
@@ -4631,27 +4699,32 @@ char_u *get_highlight_default(void)
/*
* Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number.
*/
-static int find_key_option(char_u *arg)
+int find_key_option_len(const char_u *arg, size_t len)
{
int key;
int modifiers;
- /*
- * Don't use get_special_key_code() for t_xx, we don't want it to call
- * add_termcap_entry().
- */
- if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3])
+ // Don't use get_special_key_code() for t_xx, we don't want it to call
+ // add_termcap_entry().
+ if (len >= 4 && arg[0] == 't' && arg[1] == '_') {
key = TERMCAP2KEY(arg[2], arg[3]);
- else {
- --arg; /* put arg at the '<' */
+ } else {
+ arg--; // put arg at the '<'
modifiers = 0;
- key = find_special_key(&arg, &modifiers, TRUE, TRUE);
- if (modifiers) /* can't handle modifiers here */
+ key = find_special_key(&arg, len + 1, &modifiers, true, true);
+ if (modifiers) { // can't handle modifiers here
key = 0;
+ }
}
return key;
}
+static int find_key_option(const char_u *arg)
+{
+ return find_key_option_len(arg, STRLEN(arg));
+}
+
+
/*
* if 'all' == 0: show changed options
* if 'all' == 1: show all normal options
@@ -4712,9 +4785,10 @@ showoptions (
option_value2string(p, opt_flags);
len = (int)STRLEN(p->fullname) + vim_strsize(NameBuff) + 1;
}
- if ((len <= INC - GAP && run == 1) ||
- (len > INC - GAP && run == 2))
+ if ((len <= INC - GAP && run == 1)
+ || (len > INC - GAP && run == 2)) {
items[item_count++] = p;
+ }
}
}
@@ -4903,18 +4977,15 @@ int makeset(FILE *fd, int opt_flags, int local_only)
} else { /* P_STRING */
int do_endif = FALSE;
- /* Don't set 'syntax' and 'filetype' again if the value is
- * already right, avoids reloading the syntax file. */
- if (
- p->indir == PV_SYN
- ||
- p->indir == PV_FT
- ) {
+ // Don't set 'syntax' and 'filetype' again if the value is
+ // already right, avoids reloading the syntax file.
+ if (p->indir == PV_SYN || p->indir == PV_FT) {
if (fprintf(fd, "if &%s != '%s'", p->fullname,
- *(char_u **)(varp)) < 0
- || put_eol(fd) < 0)
+ *(char_u **)(varp)) < 0
+ || put_eol(fd) < 0) {
return FAIL;
- do_endif = TRUE;
+ }
+ do_endif = true;
}
if (put_setstring(fd, cmd, p->fullname, (char_u **)varp,
(p->flags & P_EXPAND) != 0) == FAIL)
@@ -5087,6 +5158,10 @@ void unset_global_local_option(char *name, void *from)
case PV_TAGS:
clear_string_option(&buf->b_p_tags);
break;
+ case PV_TC:
+ clear_string_option(&buf->b_p_tc);
+ buf->b_tc_flags = 0;
+ break;
case PV_DEF:
clear_string_option(&buf->b_p_def);
break;
@@ -5140,6 +5215,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
case PV_PATH: return (char_u *)&(curbuf->b_p_path);
case PV_AR: return (char_u *)&(curbuf->b_p_ar);
case PV_TAGS: return (char_u *)&(curbuf->b_p_tags);
+ case PV_TC: return (char_u *)&(curbuf->b_p_tc);
case PV_DEF: return (char_u *)&(curbuf->b_p_def);
case PV_INC: return (char_u *)&(curbuf->b_p_inc);
case PV_DICT: return (char_u *)&(curbuf->b_p_dict);
@@ -5177,6 +5253,8 @@ static char_u *get_varp(vimoption_T *p)
? (char_u *)&(curbuf->b_p_ar) : p->var;
case PV_TAGS: return *curbuf->b_p_tags != NUL
? (char_u *)&(curbuf->b_p_tags) : p->var;
+ case PV_TC: return *curbuf->b_p_tc != NUL
+ ? (char_u *)&(curbuf->b_p_tc) : p->var;
case PV_BKC: return *curbuf->b_p_bkc != NUL
? (char_u *)&(curbuf->b_p_bkc) : p->var;
case PV_DEF: return *curbuf->b_p_def != NUL
@@ -5499,6 +5577,7 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_et = p_et;
buf->b_p_fixeol = p_fixeol;
buf->b_p_et_nobin = p_et_nobin;
+ buf->b_p_et_nopaste = p_et_nopaste;
buf->b_p_ml = p_ml;
buf->b_p_ml_nobin = p_ml_nobin;
buf->b_p_inf = p_inf;
@@ -5555,6 +5634,8 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_kp = empty_option;
buf->b_p_path = empty_option;
buf->b_p_tags = empty_option;
+ buf->b_p_tc = empty_option;
+ buf->b_tc_flags = 0;
buf->b_p_def = empty_option;
buf->b_p_inc = empty_option;
buf->b_p_inex = vim_strsave(p_inex);
@@ -5921,13 +6002,17 @@ option_value2string (
if (opp->flags & P_NUM) {
long wc = 0;
- if (wc_use_keyname(varp, &wc))
- STRCPY(NameBuff, get_special_key_name((int)wc, 0));
- else if (wc != 0)
- STRCPY(NameBuff, transchar((int)wc));
- else
- sprintf((char *)NameBuff, "%" PRId64, (int64_t)*(long *)varp);
- } else { /* P_STRING */
+ if (wc_use_keyname(varp, &wc)) {
+ STRLCPY(NameBuff, get_special_key_name((int)wc, 0), sizeof(NameBuff));
+ } else if (wc != 0) {
+ STRLCPY(NameBuff, transchar((int)wc), sizeof(NameBuff));
+ } else {
+ snprintf((char *)NameBuff,
+ sizeof(NameBuff),
+ "%" PRId64,
+ (int64_t)*(long *)varp);
+ }
+ } else { // P_STRING
varp = *(char_u **)(varp);
if (varp == NULL) /* just in case */
NameBuff[0] = NUL;
@@ -6137,16 +6222,14 @@ int has_format_option(int x)
return vim_strchr(curbuf->b_p_fo, x) != NULL;
}
-/*
- * Return TRUE if "x" is present in 'shortmess' option, or
- * 'shortmess' contains 'a' and "x" is present in SHM_A.
- */
-int shortmess(int x)
+/// @returns true if "x" is present in 'shortmess' option, or
+/// 'shortmess' contains 'a' and "x" is present in SHM_ALL_ABBREVIATIONS.
+bool shortmess(int x)
{
- return p_shm != NULL &&
- ( vim_strchr(p_shm, x) != NULL
- || (vim_strchr(p_shm, 'a') != NULL
- && vim_strchr((char_u *)SHM_A, x) != NULL));
+ return (p_shm != NULL
+ && (vim_strchr(p_shm, x) != NULL
+ || (vim_strchr(p_shm, 'a') != NULL
+ && vim_strchr((char_u *)SHM_ALL_ABBREVIATIONS, x) != NULL)));
}
/*
@@ -6156,6 +6239,7 @@ static void paste_option_changed(void)
{
static int old_p_paste = FALSE;
static int save_sm = 0;
+ static int save_sta = 0;
static int save_ru = 0;
static int save_ri = 0;
static int save_hkmap = 0;
@@ -6172,40 +6256,44 @@ static void paste_option_changed(void)
buf->b_p_wm_nopaste = buf->b_p_wm;
buf->b_p_sts_nopaste = buf->b_p_sts;
buf->b_p_ai_nopaste = buf->b_p_ai;
+ buf->b_p_et_nopaste = buf->b_p_et;
}
- /* save global options */
+ // save global options
save_sm = p_sm;
+ save_sta = p_sta;
save_ru = p_ru;
save_ri = p_ri;
save_hkmap = p_hkmap;
- /* save global values for local buffer options */
+ // save global values for local buffer options
+ p_ai_nopaste = p_ai;
+ p_et_nopaste = p_et;
+ p_sts_nopaste = p_sts;
p_tw_nopaste = p_tw;
p_wm_nopaste = p_wm;
- p_sts_nopaste = p_sts;
- p_ai_nopaste = p_ai;
}
- /*
- * Always set the option values, also when 'paste' is set when it is
- * already on.
- */
- /* set options for each buffer */
+ // Always set the option values, also when 'paste' is set when it is
+ // already on.
+ // set options for each buffer
FOR_ALL_BUFFERS(buf) {
- buf->b_p_tw = 0; /* textwidth is 0 */
- buf->b_p_wm = 0; /* wrapmargin is 0 */
- buf->b_p_sts = 0; /* softtabstop is 0 */
- buf->b_p_ai = 0; /* no auto-indent */
- }
-
- /* set global options */
- p_sm = 0; /* no showmatch */
- if (p_ru)
- status_redraw_all(); /* redraw to remove the ruler */
- p_ru = 0; /* no ruler */
- p_ri = 0; /* no reverse insert */
- p_hkmap = 0; /* no Hebrew keyboard */
- /* set global values for local buffer options */
+ buf->b_p_tw = 0; // textwidth is 0
+ buf->b_p_wm = 0; // wrapmargin is 0
+ buf->b_p_sts = 0; // softtabstop is 0
+ buf->b_p_ai = 0; // no auto-indent
+ buf->b_p_et = 0; // no expandtab
+ }
+
+ // set global options
+ p_sm = 0; // no showmatch
+ p_sta = 0; // no smarttab
+ if (p_ru) {
+ status_redraw_all(); // redraw to remove the ruler
+ }
+ p_ru = 0; // no ruler
+ p_ri = 0; // no reverse insert
+ p_hkmap = 0; // no Hebrew keyboard
+ // set global values for local buffer options
p_tw = 0;
p_wm = 0;
p_sts = 0;
@@ -6221,20 +6309,24 @@ static void paste_option_changed(void)
buf->b_p_wm = buf->b_p_wm_nopaste;
buf->b_p_sts = buf->b_p_sts_nopaste;
buf->b_p_ai = buf->b_p_ai_nopaste;
+ buf->b_p_et = buf->b_p_et_nopaste;
}
/* restore global options */
p_sm = save_sm;
- if (p_ru != save_ru)
- status_redraw_all(); /* redraw to draw the ruler */
+ p_sta = save_sta;
+ if (p_ru != save_ru) {
+ status_redraw_all(); // redraw to draw the ruler
+ }
p_ru = save_ru;
p_ri = save_ri;
p_hkmap = save_hkmap;
- /* set global values for local buffer options */
+ // set global values for local buffer options
+ p_ai = p_ai_nopaste;
+ p_et = p_et_nopaste;
+ p_sts = p_sts_nopaste;
p_tw = p_tw_nopaste;
p_wm = p_wm_nopaste;
- p_sts = p_sts_nopaste;
- p_ai = p_ai_nopaste;
}
old_p_paste = p_paste;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 11b5e31f77..904e97f8ca 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "nvim/types.h"
+#include "nvim/macros.h" // For EXTERN
// option_defs.h: definition of global variables for settable options
@@ -151,26 +152,42 @@
#define COCU_ALL "nvic" /* flags for 'concealcursor' */
-/* characters for p_shm option: */
-#define SHM_RO 'r' /* readonly */
-#define SHM_MOD 'm' /* modified */
-#define SHM_FILE 'f' /* (file 1 of 2) */
-#define SHM_LAST 'i' /* last line incomplete */
-#define SHM_TEXT 'x' /* tx instead of textmode */
-#define SHM_LINES 'l' /* "L" instead of "lines" */
-#define SHM_NEW 'n' /* "[New]" instead of "[New file]" */
-#define SHM_WRI 'w' /* "[w]" instead of "written" */
-#define SHM_A "rmfixlnw" /* represented by 'a' flag */
-#define SHM_WRITE 'W' /* don't use "written" at all */
-#define SHM_TRUNC 't' /* trunctate file messages */
-#define SHM_TRUNCALL 'T' /* trunctate all messages */
-#define SHM_OVER 'o' /* overwrite file messages */
-#define SHM_OVERALL 'O' /* overwrite more messages */
-#define SHM_SEARCH 's' /* no search hit bottom messages */
-#define SHM_ATTENTION 'A' /* no ATTENTION messages */
-#define SHM_INTRO 'I' /* intro messages */
-#define SHM_COMPLETIONMENU 'c' // completion menu messages
-#define SHM_ALL "rmfixlnwaWtToOsAIc" /* all possible flags for 'shm' */
+/// characters for p_shm option:
+enum {
+ SHM_RO = 'r', ///< Readonly.
+ SHM_MOD = 'm', ///< Modified.
+ SHM_FILE = 'f', ///< (file 1 of 2)
+ SHM_LAST = 'i', ///< Last line incomplete.
+ SHM_TEXT = 'x', ///< Tx instead of textmode.
+ SHM_LINES = 'l', ///< "L" instead of "lines".
+ SHM_NEW = 'n', ///< "[New]" instead of "[New file]".
+ SHM_WRI = 'w', ///< "[w]" instead of "written".
+ SHM_ABBREVIATIONS = 'a', ///< Use abbreviations from #SHM_ALL_ABBREVIATIONS.
+ SHM_WRITE = 'W', ///< Don't use "written" at all.
+ SHM_TRUNC = 't', ///< Trunctate file messages.
+ SHM_TRUNCALL = 'T', ///< Trunctate all messages.
+ SHM_OVER = 'o', ///< Overwrite file messages.
+ SHM_OVERALL = 'O', ///< Overwrite more messages.
+ SHM_SEARCH = 's', ///< No search hit bottom messages.
+ SHM_ATTENTION = 'A', ///< No ATTENTION messages.
+ SHM_INTRO = 'I', ///< Intro messages.
+ SHM_COMPLETIONMENU = 'c', ///< Completion menu messages.
+ SHM_RECORDING = 'q', ///< Short recording message.
+ SHM_FILEINFO = 'F', ///< No file info messages.
+};
+/// Represented by 'a' flag.
+#define SHM_ALL_ABBREVIATIONS ((char_u[]) { \
+ SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \
+ 0, \
+})
+/// All possible flags for 'shm'.
+#define SHM_ALL ((char_u[]) { \
+ SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \
+ SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER, \
+ SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU, \
+ SHM_RECORDING, SHM_FILEINFO, \
+ 0, \
+})
/* characters for p_go: */
#define GO_ASEL 'a' /* autoselect */
@@ -571,41 +588,52 @@ EXTERN char_u *p_su; // 'suffixes'
EXTERN char_u *p_swb; // 'switchbuf'
EXTERN unsigned swb_flags;
#ifdef IN_OPTION_C
-static char *(p_swb_values[]) = {"useopen", "usetab", "split", "newtab", NULL};
+static char *(p_swb_values[]) =
+ { "useopen", "usetab", "split", "newtab", "vsplit", NULL };
#endif
#define SWB_USEOPEN 0x001
#define SWB_USETAB 0x002
#define SWB_SPLIT 0x004
#define SWB_NEWTAB 0x008
-EXTERN int p_tbs; /* 'tagbsearch' */
-EXTERN long p_tl; /* 'taglength' */
-EXTERN int p_tr; /* 'tagrelative' */
-EXTERN char_u *p_tags; /* 'tags' */
-EXTERN int p_tgst; /* 'tagstack' */
-EXTERN int p_tbidi; /* 'termbidi' */
-EXTERN int p_terse; /* 'terse' */
-EXTERN int p_to; /* 'tildeop' */
-EXTERN int p_timeout; /* 'timeout' */
-EXTERN long p_tm; /* 'timeoutlen' */
-EXTERN int p_title; /* 'title' */
-EXTERN long p_titlelen; /* 'titlelen' */
-EXTERN char_u *p_titleold; /* 'titleold' */
-EXTERN char_u *p_titlestring; /* 'titlestring' */
-EXTERN char_u *p_tsr; /* 'thesaurus' */
-EXTERN int p_ttimeout; /* 'ttimeout' */
-EXTERN long p_ttm; /* 'ttimeoutlen' */
-EXTERN char_u *p_udir; /* 'undodir' */
-EXTERN long p_ul; /* 'undolevels' */
-EXTERN long p_ur; /* 'undoreload' */
-EXTERN long p_uc; /* 'updatecount' */
-EXTERN long p_ut; /* 'updatetime' */
-EXTERN char_u *p_fcs; /* 'fillchar' */
-EXTERN char_u *p_shada; /* 'shada' */
-EXTERN char_u *p_vdir; /* 'viewdir' */
-EXTERN char_u *p_vop; /* 'viewoptions' */
-EXTERN unsigned vop_flags; /* uses SSOP_ flags */
-EXTERN int p_vb; /* 'visualbell' */
-EXTERN char_u *p_ve; /* 'virtualedit' */
+#define SWB_VSPLIT 0x010
+EXTERN int p_tbs; ///< 'tagbsearch'
+EXTERN char_u *p_tc; ///< 'tagcase'
+EXTERN unsigned tc_flags; ///< flags from 'tagcase'
+#ifdef IN_OPTION_C
+static char *(p_tc_values[]) = { "followic", "ignore", "match", NULL };
+#endif
+#define TC_FOLLOWIC 0x01
+#define TC_IGNORE 0x02
+#define TC_MATCH 0x04
+EXTERN long p_tl; ///< 'taglength'
+EXTERN int p_tr; ///< 'tagrelative'
+EXTERN char_u *p_tags; ///< 'tags'
+EXTERN int p_tgst; ///< 'tagstack'
+EXTERN int p_tbidi; ///< 'termbidi'
+EXTERN int p_terse; ///< 'terse'
+EXTERN int p_to; ///< 'tildeop'
+EXTERN int p_timeout; ///< 'timeout'
+EXTERN long p_tm; ///< 'timeoutlen'
+EXTERN int p_title; ///< 'title'
+EXTERN long p_titlelen; ///< 'titlelen'
+EXTERN char_u *p_titleold; ///< 'titleold'
+EXTERN char_u *p_titlestring; ///< 'titlestring'
+EXTERN char_u *p_tsr; ///< 'thesaurus'
+EXTERN bool p_tgc; ///< 'termguicolors'
+EXTERN int p_ttimeout; ///< 'ttimeout'
+EXTERN long p_ttm; ///< 'ttimeoutlen'
+EXTERN char_u *p_udir; ///< 'undodir'
+EXTERN long p_ul; ///< 'undolevels'
+EXTERN long p_ur; ///< 'undoreload'
+EXTERN long p_uc; ///< 'updatecount'
+EXTERN long p_ut; ///< 'updatetime'
+EXTERN char_u *p_fcs; ///< 'fillchar'
+EXTERN char_u *p_shada; ///< 'shada'
+EXTERN char_u *p_vdir; ///< 'viewdir'
+EXTERN char_u *p_vop; ///< 'viewoptions'
+EXTERN unsigned vop_flags; ///< uses SSOP_ flags
+EXTERN int p_vb; ///< 'visualbell'
+EXTERN char_u *p_ve; ///< 'virtualedit'
EXTERN unsigned ve_flags;
# ifdef IN_OPTION_C
static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", NULL};
@@ -718,6 +746,7 @@ enum {
, BV_SW
, BV_SWF
, BV_TAGS
+ , BV_TC
, BV_TS
, BV_TW
, BV_TX
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 3dd37cb5dc..218e34f595 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -15,7 +15,7 @@
-- }
-- }
-- types: bool, number, string
--- lists: (nil), comma, flags, flagscomma
+-- lists: (nil), comma, onecomma, flags, flagscomma
-- scopes: global, buffer, window
-- redraw options: statuslines, current_window, current_buffer, all_windows,
-- everything, curswant
@@ -133,7 +133,7 @@ return {
},
{
full_name='backspace', abbreviation='bs',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vim=true,
varname='p_bs',
@@ -149,7 +149,7 @@ return {
},
{
full_name='backupcopy', abbreviation='bkc',
- type='string', list='comma', scope={'global', 'buffer'},
+ type='string', list='onecomma', scope={'global', 'buffer'},
deny_duplicates=true,
vim=true,
varname='p_bkc',
@@ -161,7 +161,7 @@ return {
},
{
full_name='backupdir', abbreviation='bdir',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
secure=true,
vi_def=true,
@@ -179,7 +179,7 @@ return {
},
{
full_name='backupskip', abbreviation='bsk',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
vi_def=true,
varname='p_bsk',
defaults={if_true={vi=""}}
@@ -227,7 +227,7 @@ return {
},
{
full_name='breakindentopt', abbreviation='briopt',
- type='string', list='comma', scope={'window'},
+ type='string', list='onecomma', scope={'window'},
deny_duplicates=true,
vi_def=true,
alloced=true,
@@ -268,7 +268,7 @@ return {
},
{
full_name='casemap', abbreviation='cmp',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_cmp',
@@ -307,7 +307,7 @@ return {
},
{
full_name='cinkeys', abbreviation='cink',
- type='string', list='comma', scope={'buffer'},
+ type='string', list='onecomma', scope={'buffer'},
deny_duplicates=true,
vi_def=true,
alloced=true,
@@ -316,7 +316,7 @@ return {
},
{
full_name='cinoptions', abbreviation='cino',
- type='string', list='comma', scope={'buffer'},
+ type='string', list='onecomma', scope={'buffer'},
deny_duplicates=true,
vi_def=true,
alloced=true,
@@ -325,7 +325,7 @@ return {
},
{
full_name='cinwords', abbreviation='cinw',
- type='string', list='comma', scope={'buffer'},
+ type='string', list='onecomma', scope={'buffer'},
deny_duplicates=true,
vi_def=true,
alloced=true,
@@ -334,7 +334,7 @@ return {
},
{
full_name='clipboard', abbreviation='cb',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_cb',
@@ -357,7 +357,7 @@ return {
},
{
full_name='colorcolumn', abbreviation='cc',
- type='string', list='comma', scope={'window'},
+ type='string', list='onecomma', scope={'window'},
deny_duplicates=true,
vi_def=true,
redraw={'current_window'},
@@ -375,7 +375,7 @@ return {
},
{
full_name='comments', abbreviation='com',
- type='string', list='comma', scope={'buffer'},
+ type='string', list='onecomma', scope={'buffer'},
deny_duplicates=true,
vi_def=true,
alloced=true,
@@ -403,7 +403,7 @@ return {
},
{
full_name='complete', abbreviation='cpt',
- type='string', list='comma', scope={'buffer'},
+ type='string', list='onecomma', scope={'buffer'},
deny_duplicates=true,
alloced=true,
varname='p_cpt',
@@ -435,7 +435,7 @@ return {
},
{
full_name='completeopt', abbreviation='cot',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_cot',
@@ -483,7 +483,7 @@ return {
},
{
full_name='cscopequickfix', abbreviation='csqf',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_csqf',
@@ -568,7 +568,7 @@ return {
},
{
full_name='dictionary', abbreviation='dict',
- type='string', list='comma', scope={'global', 'buffer'},
+ type='string', list='onecomma', scope={'global', 'buffer'},
deny_duplicates=true,
vi_def=true,
expand=true,
@@ -594,7 +594,7 @@ return {
},
{
full_name='diffopt', abbreviation='dip',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
alloced=true,
@@ -612,7 +612,7 @@ return {
},
{
full_name='directory', abbreviation='dir',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
secure=true,
vi_def=true,
@@ -622,7 +622,7 @@ return {
},
{
full_name='display', abbreviation='dy',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vim=true,
redraw={'all_windows'},
@@ -696,7 +696,7 @@ return {
},
{
full_name='errorformat', abbreviation='efm',
- type='string', list='comma', scope={'global', 'buffer'},
+ type='string', list='onecomma', scope={'global', 'buffer'},
deny_duplicates=true,
vi_def=true,
varname='p_efm',
@@ -711,7 +711,7 @@ return {
},
{
full_name='eventignore', abbreviation='ei',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_ei',
@@ -745,7 +745,7 @@ return {
},
{
full_name='fileencodings', abbreviation='fencs',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
vi_def=true,
varname='p_fencs',
defaults={if_true={vi="ucs-bom,utf-8,default,latin1"}}
@@ -762,7 +762,7 @@ return {
},
{
full_name='fileformats', abbreviation='ffs',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vim=true,
varname='p_ffs',
@@ -791,7 +791,7 @@ return {
},
{
full_name='fillchars', abbreviation='fcs',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
redraw={'all_windows'},
@@ -815,7 +815,7 @@ return {
},
{
full_name='foldclose', abbreviation='fcl',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
redraw={'current_window'},
@@ -871,7 +871,7 @@ return {
},
{
full_name='foldmarker', abbreviation='fmr',
- type='string', list='comma', scope={'window'},
+ type='string', list='onecomma', scope={'window'},
deny_duplicates=true,
vi_def=true,
vim=true,
@@ -904,7 +904,7 @@ return {
},
{
full_name='foldopen', abbreviation='fdo',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
redraw={'curswant'},
@@ -972,7 +972,7 @@ return {
},
{
full_name='grepformat', abbreviation='gfm',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_gefm',
@@ -995,7 +995,7 @@ return {
},
{
full_name='guicursor', abbreviation='gcr',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_guicursor',
@@ -1003,7 +1003,7 @@ return {
},
{
full_name='guifont', abbreviation='gfn',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
redraw={'everything'},
@@ -1011,14 +1011,14 @@ return {
},
{
full_name='guifontset', abbreviation='gfs',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
vi_def=true,
redraw={'everything'},
enable_if=false,
},
{
full_name='guifontwide', abbreviation='gfw',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
redraw={'everything'},
@@ -1070,7 +1070,7 @@ return {
},
{
full_name='helplang', abbreviation='hlg',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
vi_def=true,
varname='p_hlg',
defaults={if_true={vi=""}}
@@ -1084,7 +1084,7 @@ return {
},
{
full_name='highlight', abbreviation='hl',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
redraw={'everything'},
@@ -1213,7 +1213,7 @@ return {
},
{
full_name='indentkeys', abbreviation='indk',
- type='string', list='comma', scope={'buffer'},
+ type='string', list='onecomma', scope={'buffer'},
deny_duplicates=true,
vi_def=true,
alloced=true,
@@ -1297,7 +1297,7 @@ return {
},
{
full_name='keymodel', abbreviation='km',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_km',
@@ -1316,7 +1316,7 @@ return {
},
{
full_name='langmap', abbreviation='lmap',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
secure=true,
vi_def=true,
@@ -1385,7 +1385,7 @@ return {
},
{
full_name='lispwords', abbreviation='lw',
- type='string', list='comma', scope={'global', 'buffer'},
+ type='string', list='onecomma', scope={'global', 'buffer'},
deny_duplicates=true,
vi_def=true,
varname='p_lispwords', pv_name='p_lw',
@@ -1400,7 +1400,7 @@ return {
},
{
full_name='listchars', abbreviation='lcs',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vim=true,
redraw={'all_windows'},
@@ -1441,7 +1441,7 @@ return {
},
{
full_name='matchpairs', abbreviation='mps',
- type='string', list='comma', scope={'buffer'},
+ type='string', list='onecomma', scope={'buffer'},
deny_duplicates=true,
vi_def=true,
alloced=true,
@@ -1581,7 +1581,7 @@ return {
},
{
full_name='mouseshape', abbreviation='mouses',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
enable_if=false,
@@ -1595,7 +1595,7 @@ return {
},
{
full_name='nrformats', abbreviation='nf',
- type='string', list='comma', scope={'buffer'},
+ type='string', list='onecomma', scope={'buffer'},
deny_duplicates=true,
alloced=true,
varname='p_nf',
@@ -1762,7 +1762,7 @@ return {
},
{
full_name='printoptions', abbreviation='popt',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_popt',
@@ -1877,7 +1877,7 @@ return {
},
{
full_name='runtimepath', abbreviation='rtp',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
secure=true,
vi_def=true,
@@ -1919,7 +1919,7 @@ return {
},
{
full_name='scrollopt', abbreviation='sbo',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_sbo',
@@ -1949,7 +1949,7 @@ return {
},
{
full_name='selectmode', abbreviation='slm',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_slm',
@@ -1957,7 +1957,7 @@ return {
},
{
full_name='sessionoptions', abbreviation='ssop',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vim=true,
varname='p_ssop',
@@ -1968,7 +1968,7 @@ return {
},
{
full_name='shada', abbreviation='sd',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
secure=true,
varname='p_shada',
@@ -2192,7 +2192,7 @@ return {
},
{
full_name='spellfile', abbreviation='spf',
- type='string', list='comma', scope={'buffer'},
+ type='string', list='onecomma', scope={'buffer'},
secure=true,
vi_def=true,
alloced=true,
@@ -2202,7 +2202,7 @@ return {
},
{
full_name='spelllang', abbreviation='spl',
- type='string', list='comma', scope={'buffer'},
+ type='string', list='onecomma', scope={'buffer'},
vi_def=true,
alloced=true,
expand=true,
@@ -2212,7 +2212,7 @@ return {
},
{
full_name='spellsuggest', abbreviation='sps',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
secure=true,
vi_def=true,
expand=true,
@@ -2252,7 +2252,7 @@ return {
},
{
full_name='suffixes', abbreviation='su',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_su',
@@ -2260,7 +2260,7 @@ return {
},
{
full_name='suffixesadd', abbreviation='sua',
- type='string', list='comma', scope={'buffer'},
+ type='string', list='onecomma', scope={'buffer'},
deny_duplicates=true,
vi_def=true,
alloced=true,
@@ -2277,7 +2277,7 @@ return {
},
{
full_name='switchbuf', abbreviation='swb',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_swb',
@@ -2332,6 +2332,13 @@ return {
defaults={if_true={vi=true}}
},
{
+ full_name='tagcase', abbreviation='tc',
+ type='string', scope={'global', 'buffer'},
+ vim=true,
+ varname='p_tc',
+ defaults={if_true={vi="followic", vim="followic"}}
+ },
+ {
full_name='taglength', abbreviation='tl',
type='number', scope={'global'},
vi_def=true,
@@ -2347,7 +2354,7 @@ return {
},
{
full_name='tags', abbreviation='tag',
- type='string', list='comma', scope={'global', 'buffer'},
+ type='string', list='onecomma', scope={'global', 'buffer'},
deny_duplicates=true,
vi_def=true,
expand=true,
@@ -2376,6 +2383,14 @@ return {
defaults={if_true={vi=""}}
},
{
+ full_name='termguicolors', abbreviation='tgc',
+ type='bool', scope={'global'},
+ vi_def=false,
+ redraw={'everything'},
+ varname='p_tgc',
+ defaults={if_true={vi=false}}
+ },
+ {
full_name='terse',
type='bool', scope={'global'},
vi_def=true,
@@ -2393,7 +2408,7 @@ return {
},
{
full_name='thesaurus', abbreviation='tsr',
- type='string', list='comma', scope={'global', 'buffer'},
+ type='string', list='onecomma', scope={'global', 'buffer'},
deny_duplicates=true,
vi_def=true,
expand=true,
@@ -2478,7 +2493,7 @@ return {
},
{
full_name='undodir', abbreviation='udir',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
secure=true,
vi_def=true,
@@ -2549,7 +2564,7 @@ return {
},
{
full_name='viewoptions', abbreviation='vop',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_vop',
@@ -2557,7 +2572,7 @@ return {
},
{
full_name='viminfo', abbreviation='vi',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
secure=true,
varname='p_shada',
@@ -2565,7 +2580,7 @@ return {
},
{
full_name='virtualedit', abbreviation='ve',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
vim=true,
@@ -2610,7 +2625,7 @@ return {
},
{
full_name='wildignore', abbreviation='wig',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
varname='p_wig',
@@ -2632,7 +2647,7 @@ return {
},
{
full_name='wildmode', abbreviation='wim',
- type='string', list='comma', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vim=true,
varname='p_wim',
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index c1804067e9..edc430410c 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -147,7 +147,7 @@ static char_u *homedir = NULL;
void init_homedir(void)
{
- /* In case we are called a second time (when 'encoding' changes). */
+ // In case we are called a second time (when 'encoding' changes).
xfree(homedir);
homedir = NULL;
@@ -176,16 +176,16 @@ void init_homedir(void)
if (var != NULL) {
#ifdef UNIX
- /*
- * Change to the directory and get the actual path. This resolves
- * links. Don't do it when we can't return.
- */
+ // Change to the directory and get the actual path. This resolves
+ // links. Don't do it when we can't return.
if (os_dirname(NameBuff, MAXPATHL) == OK
&& os_chdir((char *)NameBuff) == 0) {
- if (!os_chdir((char *)var) && os_dirname(IObuff, IOSIZE) == OK)
+ if (!os_chdir((char *)var) && os_dirname(IObuff, IOSIZE) == OK) {
var = IObuff;
- if (os_chdir((char *)NameBuff) != 0)
+ }
+ if (os_chdir((char *)NameBuff) != 0) {
EMSG(_(e_prev_dir));
+ }
}
#endif
homedir = vim_strsave(var);
@@ -239,31 +239,49 @@ void expand_env(char_u *src, char_u *dst, int dstlen)
/// "~/" is also expanded, using $HOME. For Unix "~user/" is expanded.
/// Skips over "\ ", "\~" and "\$" (not for Win32 though).
/// If anything fails no expansion is done and dst equals src.
-/// startstr recognize the start of a new name, for '~' expansion.
+/// prefix recognize the start of a new name, for '~' expansion.
/// @param srcp Input string e.g. "$HOME/vim.hlp"
/// @param dst Where to put the result
/// @param dstlen Maximum length of the result
/// @param esc Should we escape spaces in expanded variables?
/// @param one Should we expand more than one '~'?
-/// @param startstr Common prefix for paths, can be NULL
-void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
- char_u *startstr)
+/// @param prefix Common prefix for paths, can be NULL
+void expand_env_esc(char_u *restrict srcp,
+ char_u *restrict dst,
+ int dstlen,
+ bool esc,
+ bool one,
+ char_u *prefix)
{
- char_u *src;
char_u *tail;
- int c;
char_u *var;
bool copy_char;
bool mustfree; // var was allocated, need to free it later
bool at_start = true; // at start of a name
- int startstr_len = 0;
- if (startstr != NULL)
- startstr_len = (int)STRLEN(startstr);
+ int prefix_len = (prefix == NULL) ? 0 : (int)STRLEN(prefix);
- src = skipwhite(srcp);
- --dstlen; // leave one char space for "\,"
+ char_u *src = skipwhite(srcp);
+ dstlen--; // leave one char space for "\,"
while (*src && dstlen > 0) {
+ // Skip over `=expr`.
+ if (src[0] == '`' && src[1] == '=') {
+ var = src;
+ src += 2;
+ (void)skip_expr(&src);
+ if (*src == '`') {
+ src++;
+ }
+ size_t len = (size_t)(src - var);
+ if (len > (size_t)dstlen) {
+ len = (size_t)dstlen;
+ }
+ memcpy((char *)dst, (char *)var, len);
+ dst += len;
+ dstlen -= (int)len;
+ continue;
+ }
+
copy_char = true;
if ((*src == '$') || (*src == '~' && at_start)) {
mustfree = false;
@@ -273,14 +291,15 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
if (*src != '~') { // environment var
tail = src + 1;
var = dst;
- c = dstlen - 1;
+ int c = dstlen - 1;
#ifdef UNIX
// Unix has ${var-name} type environment vars
if (*tail == '{' && !vim_isIDc('{')) {
- tail++; /* ignore '{' */
- while (c-- > 0 && *tail && *tail != '}')
+ tail++; // ignore '{'
+ while (c-- > 0 && *tail != NUL && *tail != '}') {
*var++ = *tail++;
+ }
} else // NOLINT
#endif
{
@@ -304,7 +323,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
#if defined(UNIX)
}
#endif
- } else if ( src[1] == NUL /* home directory */
+ } else if (src[1] == NUL // home directory
|| vim_ispathsep(src[1])
|| vim_strchr((char_u *)" ,\t\n", src[1]) != NULL) {
var = homedir;
@@ -314,12 +333,13 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
// Copy ~user to dst[], so we can put a NUL after it.
tail = src;
var = dst;
- c = dstlen - 1;
- while ( c-- > 0
- && *tail
- && vim_isfilec(*tail)
- && !vim_ispathsep(*tail))
+ int c = dstlen - 1;
+ while (c-- > 0
+ && *tail
+ && vim_isfilec(*tail)
+ && !vim_ispathsep(*tail)) {
*var++ = *tail++;
+ }
*var = NUL;
// Use os_get_user_directory() to get the user directory.
// If this function fails, the shell is used to
@@ -327,8 +347,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
// does not support ~user (old versions of /bin/sh).
var = (char_u *)os_get_user_directory((char *)dst + 1);
mustfree = true;
- if (var == NULL)
- {
+ if (var == NULL) {
expand_T xpc;
ExpandInit(&xpc);
@@ -364,8 +383,9 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
if (esc && var != NULL && vim_strpbrk(var, (char_u *)" \t") != NULL) {
char_u *p = vim_strsave_escaped(var, (char_u *)" \t");
- if (mustfree)
+ if (mustfree) {
xfree(var);
+ }
var = p;
mustfree = true;
}
@@ -374,7 +394,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
&& (STRLEN(var) + STRLEN(tail) + 1 < (unsigned)dstlen)) {
STRCPY(dst, var);
dstlen -= (int)STRLEN(var);
- c = (int)STRLEN(var);
+ int c = (int)STRLEN(var);
// if var[] ends in a path separator and tail[] starts
// with it, skip a character
if (*var != NUL && after_pathsep((char *)dst, (char *)dst + c)
@@ -387,8 +407,9 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
src = tail;
copy_char = false;
}
- if (mustfree)
+ if (mustfree) {
xfree(var);
+ }
}
if (copy_char) { // copy at least one char
@@ -405,9 +426,10 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
*dst++ = *src++;
--dstlen;
- if (startstr != NULL && src - startstr_len >= srcp
- && STRNCMP(src - startstr_len, startstr, startstr_len) == 0)
+ if (prefix != NULL && src - prefix_len >= srcp
+ && STRNCMP(src - prefix_len, prefix, prefix_len) == 0) {
at_start = true;
+ }
}
}
*dst = NUL;
@@ -434,17 +456,37 @@ static char *vim_version_dir(const char *vimdir)
return NULL;
}
-/// If the string between "p" and "pend" ends in "name/", return "pend" minus
-/// the length of "name/". Otherwise return "pend".
-static char *remove_tail(char *p, char *pend, char *name)
+/// If `dirname + "/"` precedes `pend` in the path, return the pointer to
+/// `dirname + "/" + pend`. Otherwise return `pend`.
+///
+/// Examples (path = /usr/local/share/nvim/runtime/doc/help.txt):
+///
+/// pend = help.txt
+/// dirname = doc
+/// -> doc/help.txt
+///
+/// pend = doc/help.txt
+/// dirname = runtime
+/// -> runtime/doc/help.txt
+///
+/// pend = runtime/doc/help.txt
+/// dirname = vim74
+/// -> runtime/doc/help.txt
+///
+/// @param path Path to a file
+/// @param pend A suffix of the path
+/// @param dirname The immediate path fragment before the pend
+/// @return The new pend including dirname or just pend
+static char *remove_tail(char *path, char *pend, char *dirname)
{
- size_t len = STRLEN(name) + 1;
- char *newend = pend - len;
+ size_t len = STRLEN(dirname);
+ char *new_tail = pend - len - 1;
- if (newend >= p
- && fnamencmp((char_u *)newend, (char_u *)name, len - 1) == 0
- && (newend == p || after_pathsep(p, newend)))
- return newend;
+ if (new_tail >= path
+ && fnamencmp((char_u *)new_tail, (char_u *)dirname, len) == 0
+ && (new_tail == path || after_pathsep(path, new_tail))) {
+ return new_tail;
+ }
return pend;
}
@@ -728,9 +770,10 @@ void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, bool one)
/// @param src Input file name
char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET
{
- size_t len = 3; /* space for "~/" and trailing NUL */
- if (src != NULL) /* just in case */
+ size_t len = 3; // space for "~/" and trailing NUL
+ if (src != NULL) { // just in case
len += STRLEN(src);
+ }
char_u *dst = xmalloc(len);
home_replace(buf, src, dst, (int)len, true);
return dst;
@@ -742,15 +785,15 @@ char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET
void vim_setenv(const char *name, const char *val)
{
os_setenv(name, val, 1);
- /*
- * When setting $VIMRUNTIME adjust the directory to find message
- * translations to $VIMRUNTIME/lang.
- */
+#ifndef LOCALE_INSTALL_DIR
+ // When setting $VIMRUNTIME adjust the directory to find message
+ // translations to $VIMRUNTIME/lang.
if (*val != NUL && STRICMP(name, "VIMRUNTIME") == 0) {
char *buf = (char *)concat_str((char_u *)val, (char_u *)"/lang");
- bindtextdomain(VIMPACKAGE, buf);
+ bindtextdomain(PROJECT_NAME, buf);
xfree(buf);
}
+#endif
}
@@ -766,8 +809,7 @@ char_u *get_env_name(expand_T *xp, int idx)
STRLCPY(name, envname, ENVNAMELEN);
xfree(envname);
return name;
- } else {
- return NULL;
}
+ return NULL;
}
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 2e671653ed..143a7160b0 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <assert.h>
+#include <fcntl.h>
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
@@ -59,6 +60,23 @@ int os_dirname(char_u *buf, size_t len)
return OK;
}
+/// Check if the given path is a directory and not a symlink to a directory.
+/// @return `true` if `name` is a directory and NOT a symlink to a directory.
+/// `false` if `name` is not a directory or if an error occurred.
+bool os_isrealdir(const char_u *name)
+ FUNC_ATTR_NONNULL_ALL
+{
+ uv_fs_t request;
+ if (uv_fs_lstat(&fs_loop, &request, (char *)name, NULL) != kLibuvSuccess) {
+ return false;
+ }
+ if (S_ISLNK(request.statbuf.st_mode)) {
+ return false;
+ } else {
+ return S_ISDIR(request.statbuf.st_mode);
+ }
+}
+
/// Check if the given path is a directory or not.
///
/// @return `true` if `fname` is a directory.
@@ -77,10 +95,76 @@ bool os_isdir(const char_u *name)
return true;
}
+/// Check what `name` is:
+/// @return NODE_NORMAL: file or directory (or doesn't exist)
+/// NODE_WRITABLE: writable device, socket, fifo, etc.
+/// NODE_OTHER: non-writable things
+int os_nodetype(const char *name)
+{
+#ifdef WIN32
+ // Edge case from Vim os_win32.c:
+ // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read
+ // from it later will cause Vim to hang. Thus return NODE_WRITABLE here.
+ if (STRNCMP(name, "\\\\.\\", 4) == 0) {
+ return NODE_WRITABLE;
+ }
+#endif
+
+ uv_stat_t statbuf;
+ if (0 != os_stat(name, &statbuf)) {
+ return NODE_NORMAL; // File doesn't exist.
+ }
+
+#ifndef WIN32
+ // libuv does not handle BLK and DIR in uv_handle_type.
+ // Related: https://github.com/joyent/libuv/pull/1421
+ if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode)) {
+ return NODE_NORMAL;
+ }
+ if (S_ISBLK(statbuf.st_mode)) { // block device isn't writable
+ return NODE_OTHER;
+ }
+#endif
+
+ // Vim os_win32.c:mch_nodetype does this (since patch 7.4.015):
+ // if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) {
+ // wn = enc_to_utf16(name, NULL);
+ // hFile = CreatFile(wn, ...)
+ // to get a HANDLE. But libuv just calls win32's _get_osfhandle() on the fd we
+ // give it. uv_fs_open calls fs__capture_path which does a similar dance and
+ // saves us the hassle.
+
+ int nodetype = NODE_WRITABLE;
+ int fd = os_open(name, O_RDONLY, 0);
+ switch(uv_guess_handle(fd)) {
+ case UV_TTY: // FILE_TYPE_CHAR
+ nodetype = NODE_WRITABLE;
+ break;
+ case UV_FILE: // FILE_TYPE_DISK
+ nodetype = NODE_NORMAL;
+ break;
+ case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c
+ case UV_UDP: // unix only
+ case UV_TCP: // unix only
+ case UV_UNKNOWN_HANDLE:
+ default:
+#ifdef WIN32
+ nodetype = NODE_NORMAL;
+#else
+ nodetype = NODE_WRITABLE; // Everything else is writable?
+#endif
+ break;
+ }
+
+ close(fd);
+ return nodetype;
+}
+
/// Checks if the given path represents an executable file.
///
-/// @param[in] name The name of the executable.
+/// @param[in] name Name of the executable.
/// @param[out] abspath Path of the executable, if found and not `NULL`.
+/// @param[in] use_path If 'false', only check if "name" is executable
///
/// @return `true` if `name` is executable and
/// - can be found in $PATH,
@@ -88,14 +172,18 @@ bool os_isdir(const char_u *name)
/// - is absolute.
///
/// @return `false` otherwise.
-bool os_can_exe(const char_u *name, char_u **abspath)
+bool os_can_exe(const char_u *name, char_u **abspath, bool use_path)
FUNC_ATTR_NONNULL_ARG(1)
{
- // If it's an absolute or relative path don't need to use $PATH.
- if (path_is_absolute_path(name) ||
- (name[0] == '.' && (name[1] == '/' ||
- (name[1] == '.' && name[2] == '/')))) {
- if (is_executable(name)) {
+ // when use_path is false or if it's an absolute or relative path don't
+ // need to use $PATH.
+ if (!use_path || path_is_absolute_path(name)
+ || (name[0] == '.'
+ && (name[1] == '/'
+ || (name[1] == '.' && name[2] == '/')))) {
+ // There must be a path separator, files in the current directory
+ // can't be executed
+ if (gettail_dir(name) != name && is_executable(name)) {
if (abspath != NULL) {
*abspath = save_absolute_path(name);
}
@@ -166,7 +254,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
// Glue together the given directory from $PATH with name and save into
// buf.
STRLCPY(buf, path, e - path + 1);
- append_path((char *) buf, (const char *) name, (int)buf_len);
+ append_path((char *) buf, (const char *) name, buf_len);
if (is_executable(buf)) {
// Check if the caller asked for a copy of the path.
diff --git a/src/nvim/os/fs_defs.h b/src/nvim/os/fs_defs.h
index 52b2841514..0bd9c37750 100644
--- a/src/nvim/os/fs_defs.h
+++ b/src/nvim/os/fs_defs.h
@@ -26,4 +26,10 @@ typedef struct {
/// negative libuv error codes are returned by a number of os functions.
#define os_strerror uv_strerror
+// Values returned by os_nodetype()
+#define NODE_NORMAL 0 // file or directory, check with os_isdir()
+#define NODE_WRITABLE 1 // something we can write to (character
+ // device, fifo, socket, ..)
+#define NODE_OTHER 2 // non-writable thing (e.g., block device)
+
#endif // NVIM_OS_FS_DEFS_H
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index e632544856..7687b14f02 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -175,8 +175,9 @@ size_t input_enqueue(String keys)
char *ptr = keys.data, *end = ptr + keys.size;
while (rbuffer_space(input_buffer) >= 6 && ptr < end) {
- uint8_t buf[6] = {0};
- unsigned int new_size = trans_special((uint8_t **)&ptr, buf, true);
+ uint8_t buf[6] = { 0 };
+ unsigned int new_size = trans_special((const uint8_t **)&ptr, keys.size,
+ buf, true);
if (new_size) {
new_size = handle_mouse_event(&ptr, buf, new_size);
@@ -250,6 +251,14 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
int col, row, advance;
if (sscanf(*ptr, "<%d,%d>%n", &col, &row, &advance) != EOF && advance) {
if (col >= 0 && row >= 0) {
+ // Make sure the mouse position is valid. Some terminals may
+ // return weird values.
+ if (col >= Columns) {
+ col = (int)Columns - 1;
+ }
+ if (row >= Rows) {
+ row = (int)Rows - 1;
+ }
mouse_row = row;
mouse_col = col;
}
@@ -394,9 +403,9 @@ static int push_event_key(uint8_t *buf, int maxlen)
// Check if there's pending input
static bool input_ready(void)
{
- return typebuf_was_filled || // API call filled typeahead
- rbuffer_size(input_buffer) || // Input buffer filled
- pending_events(); // Events must be processed
+ return (typebuf_was_filled // API call filled typeahead
+ || rbuffer_size(input_buffer) // Input buffer filled
+ || pending_events()); // Events must be processed
}
// Exit because of an input read error.
diff --git a/src/nvim/os/mem.c b/src/nvim/os/mem.c
index 5e483c0c3d..871ece7a0e 100644
--- a/src/nvim/os/mem.c
+++ b/src/nvim/os/mem.c
@@ -8,5 +8,5 @@
uint64_t os_get_total_mem_kib(void)
{
// Convert bytes to KiB.
- return uv_get_total_memory() >> 10;
+ return uv_get_total_memory() / 1024;
}
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index c9631a434c..81ceb919c4 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -22,9 +22,9 @@ static const char *xdg_env_vars[] = {
static const char *const xdg_defaults[] = {
#ifdef WIN32
// Windows
- [kXDGConfigHome] = "$LOCALAPPDATA\\nvim\\config",
- [kXDGDataHome] = "$LOCALAPPDATA\\nvim\\data",
- [kXDGCacheHome] = "$LOCALAPPDATA\\nvim\\cache",
+ [kXDGConfigHome] = "$LOCALAPPDATA",
+ [kXDGDataHome] = "$LOCALAPPDATA",
+ [kXDGCacheHome] = "$TEMP",
[kXDGRuntimeDir] = NULL,
[kXDGConfigDirs] = NULL,
[kXDGDataDirs] = NULL,
@@ -66,12 +66,21 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
/// @param[in] idx XDG directory to use.
///
/// @return [allocated] `{xdg_directory}/nvim`
+///
+/// In WIN32 get_xdg_home(kXDGDataHome) returns `{xdg_directory}/nvim-data` to
+/// avoid storing configuration and data files in the same path.
static char *get_xdg_home(const XDGVarType idx)
FUNC_ATTR_WARN_UNUSED_RESULT
{
char *dir = stdpaths_get_xdg_var(idx);
if (dir) {
+#if defined(WIN32)
+ dir = concat_fnames_realloc(dir,
+ (idx == kXDGDataHome ? "nvim-data" : "nvim"),
+ true);
+#else
dir = concat_fnames_realloc(dir, "nvim", true);
+#endif
}
return dir;
}
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index 242d355f77..6a29f86e79 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -1,6 +1,9 @@
#ifndef NVIM_OS_WIN_DEFS_H
#define NVIM_OS_WIN_DEFS_H
+// winsock2.h must be first to avoid incompatibilities
+// with winsock.h (included by windows.h)
+#include <winsock2.h>
#include <windows.h>
#include <sys/stat.h>
#include <io.h>
@@ -43,6 +46,8 @@
# endif
#endif
+#define BACKSLASH_IN_FILENAME
+
#ifdef _MSC_VER
typedef SSIZE_T ssize_t;
#endif
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index cb9a58cc77..2ed0c2c856 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -34,7 +34,6 @@
#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
-#include "nvim/tempfile.h"
#include "nvim/ui.h"
#include "nvim/types.h"
#include "nvim/os/os.h"
@@ -137,26 +136,6 @@ void mch_free_acl(vim_acl_T aclent)
}
#endif
-/*
- * Check what "name" is:
- * NODE_NORMAL: file or directory (or doesn't exist)
- * NODE_WRITABLE: writable device, socket, fifo, etc.
- * NODE_OTHER: non-writable things
- */
-int mch_nodetype(char_u *name)
-{
- struct stat st;
-
- if (stat((char *)name, &st))
- return NODE_NORMAL;
- if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
- return NODE_NORMAL;
- if (S_ISBLK(st.st_mode)) /* block device isn't writable */
- return NODE_OTHER;
- /* Everything else is writable? */
- return NODE_WRITABLE;
-}
-
void mch_exit(int r)
{
exiting = true;
@@ -597,9 +576,11 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file,
if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE)))
continue;
- /* Skip files that are not executable if we check for that. */
- if (!dir && (flags & EW_EXEC) && !os_can_exe((*file)[i], NULL))
+ // Skip files that are not executable if we check for that.
+ if (!dir && (flags & EW_EXEC)
+ && !os_can_exe((*file)[i], NULL, !(flags & EW_SHELLCMD))) {
continue;
+ }
p = xmalloc(STRLEN((*file)[i]) + 1 + dir);
STRCPY(p, (*file)[i]);
diff --git a/src/nvim/os_unix.h b/src/nvim/os_unix.h
index 5a3eb84ba4..d627b16ec0 100644
--- a/src/nvim/os_unix.h
+++ b/src/nvim/os_unix.h
@@ -4,12 +4,6 @@
#include "nvim/types.h" // for vim_acl_T
#include "nvim/os/shell.h"
-/* Values returned by mch_nodetype() */
-#define NODE_NORMAL 0 /* file or directory, check with os_isdir()*/
-#define NODE_WRITABLE 1 /* something we can write to (character
- device, fifo, socket, ..) */
-#define NODE_OTHER 2 /* non-writable thing (e.g., block device) */
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os_unix.h.generated.h"
#endif
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 8b9a49dfc0..41fd69f238 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -268,16 +268,13 @@ char_u *shorten_dir(char_u *str)
*/
bool dir_of_file_exists(char_u *fname)
{
- char_u *p;
- int c;
- bool retval;
-
- p = path_tail_with_sep(fname);
- if (p == fname)
+ char_u *p = path_tail_with_sep(fname);
+ if (p == fname) {
return true;
- c = *p;
+ }
+ char_u c = *p;
*p = NUL;
- retval = os_isdir(fname);
+ bool retval = os_isdir(fname);
*p = c;
return retval;
}
@@ -539,15 +536,10 @@ static size_t do_path_expand(garray_T *gap, const char_u *path,
size_t wildoff, int flags, bool didstar)
FUNC_ATTR_NONNULL_ALL
{
- char_u *buf;
- char_u *p, *s, *e;
int start_len = gap->ga_len;
- char_u *pat;
- int starts_with_dot;
- int matches;
- int len;
+ size_t len;
bool starstar = false;
- static int stardepth = 0; /* depth for "**" expansion */
+ static int stardepth = 0; // depth for "**" expansion
/* Expanding "**" may take a long time, check for CTRL-C. */
if (stardepth > 0) {
@@ -556,17 +548,16 @@ static size_t do_path_expand(garray_T *gap, const char_u *path,
return 0;
}
- /* make room for file name */
- buf = xmalloc(STRLEN(path) + BASENAMELEN + 5);
+ // Make room for file name. When doing encoding conversion the actual
+ // length may be quite a bit longer, thus use the maximum possible length.
+ char_u *buf = xmalloc(MAXPATHL);
- /*
- * Find the first part in the path name that contains a wildcard.
- * When EW_ICASE is set every letter is considered to be a wildcard.
- * Copy it into "buf", including the preceding characters.
- */
- p = buf;
- s = buf;
- e = NULL;
+ // Find the first part in the path name that contains a wildcard.
+ // When EW_ICASE is set every letter is considered to be a wildcard.
+ // Copy it into "buf", including the preceding characters.
+ char_u *p = buf;
+ char_u *s = buf;
+ char_u *e = NULL;
const char_u *path_end = path;
while (*path_end != NUL) {
/* May ignore a wildcard that has a backslash before it; it will
@@ -587,7 +578,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path,
e = p;
}
if (has_mbyte) {
- len = (*mb_ptr2len)(path_end);
+ len = (size_t)(*mb_ptr2len)(path_end);
STRNCPY(p, path_end, len);
p += len;
path_end += len;
@@ -612,9 +603,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path,
if (p[0] == '*' && p[1] == '*')
starstar = true;
- /* convert the file pattern to a regexp pattern */
- starts_with_dot = (*s == '.');
- pat = file_pat_to_reg_pat(s, e, NULL, FALSE);
+ // convert the file pattern to a regexp pattern
+ int starts_with_dot = *s == '.';
+ char_u *pat = file_pat_to_reg_pat(s, e, NULL, false);
if (pat == NULL) {
xfree(buf);
return 0;
@@ -645,9 +636,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path,
if (!didstar && stardepth < 100 && starstar && e - s == 2
&& *path_end == '/') {
STRCPY(s, path_end + 1);
- ++stardepth;
- (void)do_path_expand(gap, buf, (int)(s - buf), flags, true);
- --stardepth;
+ stardepth++;
+ (void)do_path_expand(gap, buf, (size_t)(s - buf), flags, true);
+ stardepth--;
}
*s = NUL;
@@ -656,9 +647,12 @@ static size_t do_path_expand(garray_T *gap, const char_u *path,
if (os_file_is_readable(dirpath) && os_scandir(&dir, dirpath)) {
// Find all matching entries.
char_u *name;
- scandir_next_with_dots(NULL /* initialize */);
- while((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) {
- if ((name[0] != '.' || starts_with_dot)
+ scandir_next_with_dots(NULL); // initialize
+ while ((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) {
+ if ((name[0] != '.'
+ || starts_with_dot
+ || ((flags & EW_DODOT)
+ && name[1] != NUL && (name[1] != '.' || name[2] != NUL)))
&& ((regmatch.regprog != NULL && vim_regexec(&regmatch, name, 0))
|| ((flags & EW_NOTWILD)
&& fnamencmp(path + (s - buf), name, e - s) == 0))) {
@@ -702,10 +696,11 @@ static size_t do_path_expand(garray_T *gap, const char_u *path,
xfree(buf);
vim_regfree(regmatch.regprog);
- matches = gap->ga_len - start_len;
- if (matches > 0)
+ size_t matches = (size_t)(gap->ga_len - start_len);
+ if (matches > 0) {
qsort(((char_u **)gap->ga_data) + start_len, matches,
- sizeof(char_u *), pstrcmp);
+ sizeof(char_u *), pstrcmp);
+ }
return matches;
}
@@ -735,27 +730,24 @@ static int find_previous_pathsep(char_u *path, char_u **psep)
*/
static bool is_unique(char_u *maybe_unique, garray_T *gap, int i)
{
- int candidate_len;
- int other_path_len;
- char_u **other_paths = (char_u **)gap->ga_data;
- char_u *rival;
+ char_u **other_paths = (char_u **)gap->ga_data;
for (int j = 0; j < gap->ga_len; j++) {
- if (j == i)
- continue; /* don't compare it with itself */
-
- candidate_len = (int)STRLEN(maybe_unique);
- other_path_len = (int)STRLEN(other_paths[j]);
- if (other_path_len < candidate_len)
- continue; /* it's different when it's shorter */
-
- rival = other_paths[j] + other_path_len - candidate_len;
+ if (j == i) {
+ continue; // don't compare it with itself
+ }
+ size_t candidate_len = STRLEN(maybe_unique);
+ size_t other_path_len = STRLEN(other_paths[j]);
+ if (other_path_len < candidate_len) {
+ continue; // it's different when it's shorter
+ }
+ char_u *rival = other_paths[j] + other_path_len - candidate_len;
if (fnamecmp(maybe_unique, rival) == 0
- && (rival == other_paths[j] || vim_ispathsep(*(rival - 1))))
- return false; /* match */
+ && (rival == other_paths[j] || vim_ispathsep(*(rival - 1)))) {
+ return false; // match
+ }
}
-
- return true; /* no match found */
+ return true; // no match found
}
/*
@@ -769,12 +761,8 @@ static bool is_unique(char_u *maybe_unique, garray_T *gap, int i)
*/
static void expand_path_option(char_u *curdir, garray_T *gap)
{
- char_u *path_option = *curbuf->b_p_path == NUL
- ? p_path : curbuf->b_p_path;
- char_u *buf;
- int len;
-
- buf = xmalloc(MAXPATHL);
+ char_u *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
+ char_u *buf = xmalloc(MAXPATHL);
while (*path_option != NUL) {
copy_option_part(&path_option, buf, MAXPATHL, " ,");
@@ -786,26 +774,27 @@ static void expand_path_option(char_u *curdir, garray_T *gap)
if (curbuf->b_ffname == NULL)
continue;
char_u *p = path_tail(curbuf->b_ffname);
- len = (int)(p - curbuf->b_ffname);
- if (len + (int)STRLEN(buf) >= MAXPATHL)
+ size_t len = (size_t)(p - curbuf->b_ffname);
+ if (len + STRLEN(buf) >= MAXPATHL) {
continue;
- if (buf[1] == NUL)
+ }
+ if (buf[1] == NUL) {
buf[len] = NUL;
- else
+ } else {
STRMOVE(buf + len, buf + 2);
+ }
memmove(buf, curbuf->b_ffname, len);
simplify_filename(buf);
- } else if (buf[0] == NUL)
- /* relative to current directory */
- STRCPY(buf, curdir);
- else if (path_with_url((char *)buf))
- /* URL can't be used here */
- continue;
- else if (!path_is_absolute_path(buf)) {
- /* Expand relative path to their full path equivalent */
- len = (int)STRLEN(curdir);
- if (len + (int)STRLEN(buf) + 3 > MAXPATHL)
+ } else if (buf[0] == NUL) {
+ STRCPY(buf, curdir); // relative to current directory
+ } else if (path_with_url((char *)buf)) {
+ continue; // URL can't be used here
+ } else if (!path_is_absolute_path(buf)) {
+ // Expand relative path to their full path equivalent
+ size_t len = STRLEN(curdir);
+ if (len + STRLEN(buf) + 3 > MAXPATHL) {
continue;
+ }
STRMOVE(buf + len + 1, buf);
STRCPY(buf, curdir);
buf[len] = PATHSEP;
@@ -859,31 +848,25 @@ static char_u *get_path_cutoff(char_u *fname, garray_T *gap)
*/
static void uniquefy_paths(garray_T *gap, char_u *pattern)
{
- int len;
- char_u **fnames = (char_u **)gap->ga_data;
+ char_u **fnames = (char_u **)gap->ga_data;
bool sort_again = false;
- char_u *pat;
- char_u *file_pattern;
- char_u *curdir;
regmatch_T regmatch;
garray_T path_ga;
- char_u **in_curdir = NULL;
- char_u *short_name;
+ char_u **in_curdir = NULL;
+ char_u *short_name;
ga_remove_duplicate_strings(gap);
ga_init(&path_ga, (int)sizeof(char_u *), 1);
- /*
- * We need to prepend a '*' at the beginning of file_pattern so that the
- * regex matches anywhere in the path. FIXME: is this valid for all
- * possible patterns?
- */
- len = (int)STRLEN(pattern);
- file_pattern = xmalloc(len + 2);
+ // We need to prepend a '*' at the beginning of file_pattern so that the
+ // regex matches anywhere in the path. FIXME: is this valid for all
+ // possible patterns?
+ size_t len = STRLEN(pattern);
+ char_u *file_pattern = xmalloc(len + 2);
file_pattern[0] = '*';
file_pattern[1] = NUL;
STRCAT(file_pattern, pattern);
- pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, TRUE);
+ char_u *pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, true);
xfree(file_pattern);
if (pat == NULL)
return;
@@ -894,11 +877,11 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
if (regmatch.regprog == NULL)
return;
- curdir = xmalloc(MAXPATHL);
+ char_u *curdir = xmalloc(MAXPATHL);
os_dirname(curdir, MAXPATHL);
expand_path_option(curdir, &path_ga);
- in_curdir = xcalloc(gap->ga_len, sizeof(char_u *));
+ in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char_u *));
for (int i = 0; i < gap->ga_len && !got_int; i++) {
char_u *path = fnames[i];
@@ -907,7 +890,7 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
char_u *pathsep_p;
char_u *path_cutoff;
- len = (int)STRLEN(path);
+ len = STRLEN(path);
is_in_curdir = fnamencmp(curdir, path, dir_end - path) == 0
&& curdir[dir_end - path] == NUL;
if (is_in_curdir)
@@ -998,12 +981,12 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
* "/path/file", "/path/dir/", "/path//dir", "/file"
* ^ ^ ^ ^
*/
-static char_u *gettail_dir(char_u *fname)
+char_u *gettail_dir(const char_u *fname)
{
- char_u *dir_end = fname;
- char_u *next_dir_end = fname;
+ const char_u *dir_end = fname;
+ const char_u *next_dir_end = fname;
bool look_for_sep = true;
- char_u *p;
+ const char_u *p;
for (p = fname; *p != NUL; ) {
if (vim_ispathsep(*p)) {
@@ -1018,7 +1001,7 @@ static char_u *gettail_dir(char_u *fname)
}
mb_ptr_adv(p);
}
- return dir_end;
+ return (char_u *)dir_end;
}
@@ -1112,9 +1095,8 @@ static bool has_special_wildchar(char_u *p)
int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file,
char_u ***file, int flags)
{
- int i;
garray_T ga;
- char_u *p;
+ char_u *p;
static bool recursive = false;
int add_pat;
bool did_expand_in_path = false;
@@ -1139,11 +1121,11 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file,
* avoids starting the shell for each argument separately.
* For `=expr` do use the internal function.
*/
- for (i = 0; i < num_pat; i++) {
+ for (int i = 0; i < num_pat; i++) {
if (has_special_wildchar(pat[i])
- && !(vim_backtick(pat[i]) && pat[i][1] == '=')
- )
+ && !(vim_backtick(pat[i]) && pat[i][1] == '=')) {
return mch_expand_wildcards(num_pat, pat, num_file, file, flags);
+ }
}
#endif
@@ -1154,16 +1136,21 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file,
*/
ga_init(&ga, (int)sizeof(char_u *), 30);
- for (i = 0; i < num_pat; ++i) {
+ for (int i = 0; i < num_pat; ++i) {
add_pat = -1;
p = pat[i];
- if (vim_backtick(p))
+ if (vim_backtick(p)) {
add_pat = expand_backtick(&ga, p, flags);
- else {
- /*
- * First expand environment variables, "~/" and "~user/".
- */
+ if (add_pat == -1) {
+ recursive = false;
+ FreeWild(ga.ga_len, (char_u **)ga.ga_data);
+ *num_file = 0;
+ *file = NULL;
+ return FAIL;
+ }
+ } else {
+ // First expand environment variables, "~/" and "~user/".
if (has_env_var(p) || *p == '~') {
p = expand_env_save_opt(p, true);
if (p == NULL)
@@ -1206,7 +1193,9 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file,
recursive = true;
did_expand_in_path = true;
} else {
- add_pat = path_expand(&ga, p, flags);
+ size_t tmp_add_pat = path_expand(&ga, p, flags);
+ assert(tmp_add_pat <= INT_MAX);
+ add_pat = (int)tmp_add_pat;
}
}
}
@@ -1234,7 +1223,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file,
recursive = false;
- return (ga.ga_data != NULL) ? OK : FAIL;
+ return ((flags & EW_EMPTYOK) || ga.ga_data != NULL) ? OK : FAIL;
}
@@ -1246,26 +1235,21 @@ static int vim_backtick(char_u *p)
return *p == '`' && *(p + 1) != NUL && *(p + STRLEN(p) - 1) == '`';
}
-/*
- * Expand an item in `backticks` by executing it as a command.
- * Currently only works when pat[] starts and ends with a `.
- * Returns number of file names found.
- */
-static int
-expand_backtick (
+// Expand an item in `backticks` by executing it as a command.
+// Currently only works when pat[] starts and ends with a `.
+// Returns number of file names found, -1 if an error is encountered.
+static int expand_backtick(
garray_T *gap,
char_u *pat,
int flags /* EW_* flags */
)
{
- char_u *p;
- char_u *cmd;
- char_u *buffer;
+ char_u *p;
+ char_u *buffer;
int cnt = 0;
- int i;
- /* Create the command: lop off the backticks. */
- cmd = vim_strnsave(pat + 1, (int)STRLEN(pat) - 2);
+ // Create the command: lop off the backticks.
+ char_u *cmd = vim_strnsave(pat + 1, STRLEN(pat) - 2);
if (*cmd == '=') /* `={expr}`: Expand expression */
buffer = eval_to_string(cmd + 1, &p, TRUE);
@@ -1273,8 +1257,9 @@ expand_backtick (
buffer = get_cmd_output(cmd, NULL,
(flags & EW_SILENT) ? kShellOptSilent : 0, NULL);
xfree(cmd);
- if (buffer == NULL)
- return 0;
+ if (buffer == NULL) {
+ return -1;
+ }
cmd = buffer;
while (*cmd != NUL) {
@@ -1284,7 +1269,7 @@ expand_backtick (
++p;
/* add an entry if it is not empty */
if (p > cmd) {
- i = *p;
+ char_u i = *p;
*p = NUL;
addfile(gap, cmd, flags);
*p = i;
@@ -1299,6 +1284,29 @@ expand_backtick (
return cnt;
}
+#ifdef BACKSLASH_IN_FILENAME
+/// Replace all slashes by backslashes.
+/// This used to be the other way around, but MS-DOS sometimes has problems
+/// with slashes (e.g. in a command name). We can't have mixed slashes and
+/// backslashes, because comparing file names will not work correctly. The
+/// commands that use a file name should try to avoid the need to type a
+/// backslash twice.
+/// When 'shellslash' set do it the other way around.
+/// When the path looks like a URL leave it unmodified.
+void slash_adjust(char_u *p)
+{
+ if (path_with_url(p)) {
+ return;
+ }
+ while (*p) {
+ if (*p == psepcN) {
+ *p = psepc;
+ }
+ mb_ptr_adv(p);
+ }
+}
+#endif
+
// Add a file to a file list. Accepted flags:
// EW_DIR add directories
// EW_FILE add files
@@ -1316,9 +1324,10 @@ void addfile(
FileInfo file_info;
// if the file/dir/link doesn't exist, may not add it
- if (!(flags & EW_NOTFOUND) &&
- ((flags & EW_ALLLINKS) ?
- !os_fileinfo_link((char *)f, &file_info) : !os_file_exists(f))) {
+ if (!(flags & EW_NOTFOUND)
+ && ((flags & EW_ALLLINKS)
+ ? !os_fileinfo_link((char *)f, &file_info)
+ : !os_file_exists(f))) {
return;
}
@@ -1332,9 +1341,12 @@ void addfile(
if ((isdir && !(flags & EW_DIR)) || (!isdir && !(flags & EW_FILE)))
return;
- /* If the file isn't executable, may not add it. Do accept directories. */
- if (!isdir && (flags & EW_EXEC) && !os_can_exe(f, NULL))
+ // If the file isn't executable, may not add it. Do accept directories.
+ // When invoked from expand_shellcmd() do not use $PATH.
+ if (!isdir && (flags & EW_EXEC)
+ && !os_can_exe(f, NULL, !(flags & EW_SHELLCMD))) {
return;
+ }
char_u *p = xmalloc(STRLEN(f) + 1 + isdir);
@@ -1398,9 +1410,9 @@ void simplify_filename(char_u *filename)
--p; /* strip preceding path separator */
STRMOVE(p, tail);
}
- } else if (p[0] == '.' && p[1] == '.' &&
- (vim_ispathsep(p[2]) || p[2] == NUL)) {
- /* Skip to after ".." or "../" or "..///". */
+ } else if (p[0] == '.' && p[1] == '.'
+ && (vim_ispathsep(p[2]) || p[2] == NUL)) {
+ // Skip to after ".." or "../" or "..///".
tail = p + 2;
while (vim_ispathsep(*tail))
mb_ptr_adv(tail);
@@ -1513,13 +1525,12 @@ void simplify_filename(char_u *filename)
} while (*p != NUL);
}
-static char_u *eval_includeexpr(char_u *ptr, size_t len)
+static char *eval_includeexpr(const char *const ptr, const size_t len)
{
- assert(len <= INT_MAX);
- set_vim_var_string(VV_FNAME, ptr, (int)len);
- char_u *res = eval_to_string_safe(curbuf->b_p_inex, NULL,
- was_set_insecurely((char_u *)"includeexpr",
- OPT_LOCAL));
+ set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t) len);
+ char *res = (char *) eval_to_string_safe(
+ curbuf->b_p_inex, NULL, was_set_insecurely((char_u *)"includeexpr",
+ OPT_LOCAL));
set_vim_var_string(VV_FNAME, NULL, 0);
return res;
}
@@ -1537,12 +1548,11 @@ find_file_name_in_path (
char_u *rel_fname /* file we are searching relative to */
)
{
- char_u *file_name;
- int c;
- char_u *tofree = NULL;
+ char_u *file_name;
+ char_u *tofree = NULL;
if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
- tofree = eval_includeexpr(ptr, len);
+ tofree = (char_u *) eval_includeexpr((char *) ptr, len);
if (tofree != NULL) {
ptr = tofree;
len = STRLEN(ptr);
@@ -1550,8 +1560,8 @@ find_file_name_in_path (
}
if (options & FNAME_EXP) {
- file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
- TRUE, rel_fname);
+ file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, true,
+ rel_fname);
/*
* If the file could not be found in a normal way, try applying
@@ -1559,7 +1569,7 @@ find_file_name_in_path (
*/
if (file_name == NULL
&& !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
- tofree = eval_includeexpr(ptr, len);
+ tofree = (char_u *) eval_includeexpr((char *) ptr, len);
if (tofree != NULL) {
ptr = tofree;
len = STRLEN(ptr);
@@ -1568,7 +1578,7 @@ find_file_name_in_path (
}
}
if (file_name == NULL && (options & FNAME_MESS)) {
- c = ptr[len];
+ char_u c = ptr[len];
ptr[len] = NUL;
EMSG2(_("E447: Can't find file \"%s\" in path"), ptr);
ptr[len] = c;
@@ -1627,7 +1637,7 @@ bool vim_isAbsName(char_u *name)
/// @param force is a flag to force expanding even if the path is absolute
///
/// @return FAIL for failure, OK otherwise
-int vim_FullName(const char *fname, char *buf, int len, bool force)
+int vim_FullName(const char *fname, char *buf, size_t len, bool force)
FUNC_ATTR_NONNULL_ARG(2)
{
int retval = OK;
@@ -1775,19 +1785,20 @@ bool same_directory(char_u *f1, char_u *f2)
*/
int pathcmp(const char *p, const char *q, int maxlen)
{
- int i;
+ int i, j;
int c1, c2;
const char *s = NULL;
- for (i = 0; maxlen < 0 || i < maxlen; i += MB_PTR2LEN((char_u *)p + i)) {
+ for (i = 0, j = 0; maxlen < 0 || (i < maxlen && j < maxlen);) {
c1 = PTR2CHAR((char_u *)p + i);
- c2 = PTR2CHAR((char_u *)q + i);
+ c2 = PTR2CHAR((char_u *)q + j);
/* End of "p": check if "q" also ends or just has a slash. */
if (c1 == NUL) {
if (c2 == NUL) /* full match */
return 0;
s = q;
+ i = j;
break;
}
@@ -1811,9 +1822,13 @@ int pathcmp(const char *p, const char *q, int maxlen)
return p_fic ? vim_toupper(c1) - vim_toupper(c2)
: c1 - c2; /* no match */
}
+
+ i += MB_PTR2LEN((char_u *)p + i);
+ j += MB_PTR2LEN((char_u *)q + j);
}
- if (s == NULL) /* "i" ran into "maxlen" */
+ if (s == NULL) { // "i" or "j" ran into "maxlen"
return 0;
+ }
c1 = PTR2CHAR((char_u *)s + i);
c2 = PTR2CHAR((char_u *)s + i + MB_PTR2LEN((char_u *)s + i));
@@ -1938,7 +1953,7 @@ int expand_wildcards_eval(char_u **pat, int *num_file, char_u ***file,
/// If FAIL is returned, *num_file and *file are either
/// unchanged or *num_file is set to 0 and *file is set to
/// NULL or points to "".
-int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file,
+int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files,
int flags)
{
int retval;
@@ -1946,7 +1961,7 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file,
char_u *p;
int non_suf_match; /* number without matching suffix */
- retval = gen_expand_wildcards(num_pat, pat, num_file, file, flags);
+ retval = gen_expand_wildcards(num_pat, pat, num_files, files, flags);
/* When keeping all matches, return here */
if ((flags & EW_KEEPALL) || retval == FAIL)
@@ -1958,18 +1973,20 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file,
if (*p_wig) {
char_u *ffname;
- /* check all files in (*file)[] */
- for (i = 0; i < *num_file; ++i) {
- ffname = (char_u *)FullName_save((char *)(*file)[i], FALSE);
- if (ffname == NULL) /* out of memory */
+ // check all filess in (*files)[]
+ for (i = 0; i < *num_files; i++) {
+ ffname = (char_u *)FullName_save((char *)(*files)[i], false);
+ if (ffname == NULL) { // out of memory
break;
- if (match_file_list(p_wig, (*file)[i], ffname)) {
- /* remove this matching file from the list */
- xfree((*file)[i]);
- for (j = i; j + 1 < *num_file; ++j)
- (*file)[j] = (*file)[j + 1];
- --*num_file;
- --i;
+ }
+ if (match_file_list(p_wig, (*files)[i], ffname)) {
+ // remove this matching files from the list
+ xfree((*files)[i]);
+ for (j = i; j + 1 < *num_files; j++) {
+ (*files)[j] = (*files)[j + 1];
+ }
+ (*num_files)--;
+ i--;
}
xfree(ffname);
}
@@ -1978,26 +1995,28 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file,
/*
* Move the names where 'suffixes' match to the end.
*/
- if (*num_file > 1) {
+ if (*num_files > 1) {
non_suf_match = 0;
- for (i = 0; i < *num_file; ++i) {
- if (!match_suffix((*file)[i])) {
- /*
- * Move the name without matching suffix to the front
- * of the list.
- */
- p = (*file)[i];
- for (j = i; j > non_suf_match; --j)
- (*file)[j] = (*file)[j - 1];
- (*file)[non_suf_match++] = p;
+ for (i = 0; i < *num_files; i++) {
+ if (!match_suffix((*files)[i])) {
+ //
+ // Move the name without matching suffix to the front
+ // of the list.
+ //
+ p = (*files)[i];
+ for (j = i; j > non_suf_match; j--) {
+ (*files)[j] = (*files)[j - 1];
+ }
+ (*files)[non_suf_match++] = p;
}
}
}
// Free empty array of matches
- if (*num_file == 0) {
- xfree(*file);
- *file = NULL;
+ if (*num_files == 0) {
+ xfree(*files);
+ *files = NULL;
+ return FAIL;
}
return retval;
@@ -2008,14 +2027,12 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file,
*/
int match_suffix(char_u *fname)
{
- int fnamelen, setsuflen;
- char_u *setsuf;
-#define MAXSUFLEN 30 /* maximum length of a file suffix */
+#define MAXSUFLEN 30 // maximum length of a file suffix
char_u suf_buf[MAXSUFLEN];
- fnamelen = (int)STRLEN(fname);
- setsuflen = 0;
- for (setsuf = p_su; *setsuf; ) {
+ size_t fnamelen = STRLEN(fname);
+ size_t setsuflen = 0;
+ for (char_u *setsuf = p_su; *setsuf; ) {
setsuflen = copy_option_part(&setsuf, suf_buf, MAXSUFLEN, ".,");
if (setsuflen == 0) {
char_u *tail = path_tail(fname);
@@ -2027,9 +2044,9 @@ int match_suffix(char_u *fname)
}
} else {
if (fnamelen >= setsuflen
- && fnamencmp(suf_buf, fname + fnamelen - setsuflen,
- (size_t)setsuflen) == 0)
+ && fnamencmp(suf_buf, fname + fnamelen - setsuflen, setsuflen) == 0) {
break;
+ }
setsuflen = 0;
}
}
@@ -2040,7 +2057,7 @@ int match_suffix(char_u *fname)
///
/// @param directory Directory name, relative to current directory.
/// @return `FAIL` for failure, `OK` for success.
-int path_full_dir_name(char *directory, char *buffer, int len)
+int path_full_dir_name(char *directory, char *buffer, size_t len)
{
int SUCCESS = 0;
int retval = OK;
@@ -2082,10 +2099,10 @@ int path_full_dir_name(char *directory, char *buffer, int len)
// Append to_append to path with a slash in between.
// Append to_append to path with a slash in between.
-int append_path(char *path, const char *to_append, int max_len)
+int append_path(char *path, const char *to_append, size_t max_len)
{
- int current_length = STRLEN(path);
- int to_append_length = STRLEN(to_append);
+ size_t current_length = strlen(path);
+ size_t to_append_length = strlen(to_append);
// Do not append empty strings.
if (to_append_length == 0) {
@@ -2120,12 +2137,14 @@ int append_path(char *path, const char *to_append, int max_len)
/// Expand a given file to its absolute path.
///
-/// @param fname The filename which should be expanded.
-/// @param buf Buffer to store the absolute path of `fname`.
-/// @param len Length of `buf`.
-/// @param force Also expand when `fname` is already absolute.
-/// @return `FAIL` for failure, `OK` for success.
-static int path_get_absolute_path(const char_u *fname, char_u *buf, int len, int force)
+/// @param fname filename which should be expanded.
+/// @param buf buffer to store the absolute path of "fname".
+/// @param len length of "buf".
+/// @param force also expand when "fname" is already absolute.
+///
+/// @return FAIL for failure, OK for success.
+static int path_get_absolute_path(const char_u *fname, char_u *buf,
+ size_t len, int force)
{
char_u *p;
*buf = NUL;
diff --git a/src/nvim/path.h b/src/nvim/path.h
index eac367d0ac..4e466d1b71 100644
--- a/src/nvim/path.h
+++ b/src/nvim/path.h
@@ -21,6 +21,10 @@
/* Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND
* is used when executing commands and EW_SILENT for interactive expanding. */
#define EW_ALLLINKS 0x1000 // also links not pointing to existing file
+#define EW_SHELLCMD 0x2000 // called from expand_shellcmd(), don't check
+ // if executable is in $PATH
+#define EW_DODOT 0x4000 // also files starting with a dot
+#define EW_EMPTYOK 0x8000 // no matches is not an error
/// Return value for the comparison of two files. Also @see path_full_compare.
typedef enum file_comparison {
diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt
index 6687918df4..184c4894b9 100644
--- a/src/nvim/po/CMakeLists.txt
+++ b/src/nvim/po/CMakeLists.txt
@@ -73,7 +73,7 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)
install_helper(
FILES ${moFile}
DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${name}/LC_MESSAGES
- RENAME nvim.mo)
+ RENAME ${PROJECT_NAME}.mo)
list(APPEND LANGUAGE_MO_FILES ${moFile})
endmacro()
diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po
index 8215b31e65..5b0cb2260b 100644
--- a/src/nvim/po/eo.po
+++ b/src/nvim/po/eo.po
@@ -23,8 +23,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Vim(Esperanto)\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-05-26 14:21+0200\n"
-"PO-Revision-Date: 2013-05-27 04:55+0200\n"
+"POT-Creation-Date: 2015-07-30 17:54+0200\n"
+"PO-Revision-Date: 2015-07-30 18:00+0200\n"
"Last-Translator: Dominique PELLÉ <dominique.pelle@gmail.com>\n"
"Language-Team: \n"
"Language: eo\n"
@@ -115,11 +115,6 @@ msgstr "E84: Neniu modifita bufro trovita"
msgid "E85: There is no listed buffer"
msgstr "E85: Estas neniu listigita bufro"
-#: ../buffer.c:913
-#, c-format
-msgid "E86: Buffer %<PRId64> does not exist"
-msgstr "E86: La bufro %<PRId64> ne ekzistas"
-
#: ../buffer.c:915
msgid "E87: Cannot go beyond last buffer"
msgstr "E87: Ne eblas iri preter la lastan bufron"
@@ -692,6 +687,10 @@ msgstr "E696: Mankas komo en Listo: %s"
msgid "E697: Missing end of List ']': %s"
msgstr "E697: Mankas fino de Listo ']': %s"
+#: ../eval.c:5750
+msgid "Not enough memory to set references, garbage collection aborted!"
+msgstr "Ne sufiĉa memory por valorigi referencojn, senrubigado ĉesigita!"
+
#: ../eval.c:6475
#, c-format
msgid "E720: Missing colon in Dictionary: %s"
@@ -1616,14 +1615,13 @@ msgstr "E173: %<PRId64> pliaj redaktendaj dosieroj"
msgid "E174: Command already exists: add ! to replace it"
msgstr "E174: La komando jam ekzistas: aldonu ! por anstataÅ­igi Äin"
-# DP: malfacilas traduki tion, kaj samtempe honori spacetojn
#: ../ex_docmd.c:4432
msgid ""
"\n"
-" Name Args Range Complete Definition"
+" Name Args Address Complete Definition"
msgstr ""
"\n"
-" Nomo Arg Interv Kompleto Difino"
+" Nomo Argumentoj Adreso Kompleto Difino"
#: ../ex_docmd.c:4516
msgid "No user-defined commands found"
@@ -1649,6 +1647,10 @@ msgstr "E178: Nevalida defaÅ­lta valoro de kvantoro"
msgid "E179: argument required for -complete"
msgstr "E179: argumento bezonata por -complete"
+#: ../ex_docmd.c:4933
+msgid "E179: argument required for -addr"
+msgstr "E179: argumento bezonata por -addr"
+
#: ../ex_docmd.c:4635
#, c-format
msgid "E181: Invalid attribute: %s"
@@ -1671,6 +1673,11 @@ msgstr "E841: Rezervita nomo, neuzebla por komando difinita de uzanto"
msgid "E184: No such user-defined command: %s"
msgstr "E184: Neniu komando-difinita-de-uzanto kiel: %s"
+#: ../ex_docmd.c:5516
+#, c-format
+msgid "E180: Invalid address type value: %s"
+msgstr "E180: Nevalida valoro de tipo de adreso: %s"
+
#: ../ex_docmd.c:5219
#, c-format
msgid "E180: Invalid complete value: %s"
@@ -2951,6 +2958,11 @@ msgstr "E363: Åablono uzas pli da memoro ol 'maxmempattern'"
msgid "E749: empty buffer"
msgstr "E749: malplena bufro"
+#: ../globals.h:1226
+#, c-format
+msgid "E86: Buffer %<PRId64> does not exist"
+msgstr "E86: La bufro %<PRId64> ne ekzistas"
+
#: ../globals.h:1108
msgid "E682: Invalid search pattern or delimiter"
msgstr "E682: Nevalida serĉa Åablono aÅ­ disigilo"
@@ -4599,6 +4611,11 @@ msgstr "E522: Netrovita en termcap"
msgid "E539: Illegal character <%s>"
msgstr "E539: Nevalida signo <%s>"
+#: ../option.c:2253
+#, c-format
+msgid "For option %s"
+msgstr "Por opcio %s"
+
#: ../option.c:3862
msgid "E529: Cannot set 'term' to empty string"
msgstr "E529: Ne eblas agordi 'term' al malplena ĉeno"
@@ -6552,15 +6569,15 @@ msgstr "E446: Neniu dosiernomo sub la kursoro"
#~ msgid "Reading from stdin..."
#~ msgstr "Legado el stdin..."
-#~ msgid "[blowfish]"
-#~ msgstr "[blowfish]"
-
#~ msgid "[crypted]"
#~ msgstr "[ĉifrita]"
#~ msgid "E821: File is encrypted with unknown method"
#~ msgstr "E821: Dosiero estas ĉifrata per nekonata metodo"
+#~ msgid "Warning: Using a weak encryption method; see :help 'cm'"
+#~ msgstr "Averto: uzo de malfortika ĉifrada metodo; vidu :help 'cm'"
+
#~ msgid "NetBeans disallows writes of unmodified buffers"
#~ msgstr "NetBeans malpermesas skribojn de neÅanÄitaj bufroj"
@@ -6676,8 +6693,8 @@ msgstr "E446: Neniu dosiernomo sub la kursoro"
#~ msgid "Vim: Received \"die\" request from session manager\n"
#~ msgstr "Vim: Ricevis peton \"die\" (morti) el la seanca administrilo\n"
-#~ msgid "Close"
-#~ msgstr "Fermi"
+#~ msgid "Close tab"
+#~ msgstr "Fermi langeton"
#~ msgid "New tab"
#~ msgstr "Nova langeto"
@@ -6733,9 +6750,6 @@ msgstr "E446: Neniu dosiernomo sub la kursoro"
#~ msgid "E672: Unable to open window inside MDI application"
#~ msgstr "E672: Ne eblas malfermi fenestron interne de aplikaĵo MDI"
-#~ msgid "Close tab"
-#~ msgstr "Fermi langeton"
-
#~ msgid "Open tab..."
#~ msgstr "Malfermi langeton..."
diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po
index 1a5ef991dc..8a9c86e88d 100644
--- a/src/nvim/po/es.po
+++ b/src/nvim/po/es.po
@@ -4276,7 +4276,8 @@ msgstr ""
"&Abrir para lectura únicamente\n"
"&Editar de todas formas\n"
"&Recuperar\n"
-"&Borrar&Salir\n"
+"&Borrar\n"
+"&Salir\n"
"&Abortar"
#.
diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po
index 9b2d44ce7d..41efd5c5e3 100644
--- a/src/nvim/po/fr.po
+++ b/src/nvim/po/fr.po
@@ -6,7 +6,7 @@
# FIRST AUTHOR DindinX <David.Odin@bigfoot.com> 2000.
# SECOND AUTHOR Adrien Beau <version.francaise@free.fr> 2002, 2003.
# THIRD AUTHOR David Blanchet <david.blanchet@free.fr> 2006, 2008.
-# FOURTH AUTHOR Dominique Pellé <dominique.pelle@gmail.com> 2008, 2013.
+# FOURTH AUTHOR Dominique Pellé <dominique.pelle@gmail.com> 2008, 2015.
#
# Latest translation available at:
# http://dominique.pelle.free.fr/vim-fr.php
@@ -15,8 +15,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Vim(Français)\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-05-26 14:21+0200\n"
-"PO-Revision-Date: 2013-05-27 10:22+0200\n"
+"POT-Creation-Date: 2015-07-30 17:54+0200\n"
+"PO-Revision-Date: 2015-07-30 18:00+0200\n"
"Last-Translator: Dominique Pellé <dominique.pelle@gmail.com>\n"
"Language-Team: \n"
"Language: fr\n"
@@ -113,11 +113,6 @@ msgstr "E84: Aucun tampon n'est modifié"
msgid "E85: There is no listed buffer"
msgstr "E85: Aucun tampon n'est listé"
-#: ../buffer.c:913
-#, c-format
-msgid "E86: Buffer %<PRId64> does not exist"
-msgstr "E86: Le tampon %<PRId64> n'existe pas"
-
# AB - Je ne suis pas sûr que l'on puisse obtenir ce message.
#: ../buffer.c:915
msgid "E87: Cannot go beyond last buffer"
@@ -756,6 +751,11 @@ msgstr "E696: Il manque une virgule dans la Liste %s"
msgid "E697: Missing end of List ']': %s"
msgstr "E697: Il manque ']' à la fin de la Liste %s"
+#: ../eval.c:5750
+msgid "Not enough memory to set references, garbage collection aborted!"
+msgstr ""
+"Pas assez de mémoire pour les références, arrêt du ramassage de miètes !"
+
#: ../eval.c:6475
#, c-format
msgid "E720: Missing colon in Dictionary: %s"
@@ -832,15 +832,15 @@ msgstr "E785: complete() n'est utilisable que dans le mode Insertion"
msgid "&Ok"
msgstr "&Ok"
-#: ../eval.c:8676
-#, c-format
-msgid "E737: Key already exists: %s"
-msgstr "E737: un mappage existe déjà pour %s"
-
#: ../eval.c:8692
msgid "extend() argument"
msgstr "argument de extend()"
+#: ../eval.c:9345
+#, c-format
+msgid "E737: Key already exists: %s"
+msgstr "E737: un mappage existe déjà pour %s"
+
#: ../eval.c:8915
msgid "map() argument"
msgstr "argument de map()"
@@ -1798,10 +1798,10 @@ msgstr "E174: La commande existe déjà : ajoutez ! pour la redéfinir"
#: ../ex_docmd.c:4432
msgid ""
"\n"
-" Name Args Range Complete Definition"
+" Name Args Address Complete Definition"
msgstr ""
"\n"
-" Nom Args Plage Complet. Définition"
+" Nom Args Adresse Complet. Définition"
#: ../ex_docmd.c:4516
msgid "No user-defined commands found"
@@ -1827,6 +1827,10 @@ msgstr "E178: La valeur par défaut du quantificateur est invalide"
msgid "E179: argument required for -complete"
msgstr "E179: argument requis avec -complete"
+#: ../ex_docmd.c:4933
+msgid "E179: argument required for -addr"
+msgstr "E179: argument requis avec -addr"
+
#: ../ex_docmd.c:4635
#, c-format
msgid "E181: Invalid attribute: %s"
@@ -1850,6 +1854,11 @@ msgstr ""
msgid "E184: No such user-defined command: %s"
msgstr "E184: Aucune commande %s définie par l'utilisateur"
+#: ../ex_docmd.c:5516
+#, c-format
+msgid "E180: Invalid address type value: %s"
+msgstr "E180: Valeur de type d'adresse invalide : %s"
+
#: ../ex_docmd.c:5219
#, c-format
msgid "E180: Invalid complete value: %s"
@@ -3155,6 +3164,11 @@ msgstr "E363: le motif utilise plus de mémoire que 'maxmempattern'"
msgid "E749: empty buffer"
msgstr "E749: tampon vide"
+#: ../buffer.c:1587
+#, c-format
+msgid "E86: Buffer %<PRId64> does not exist"
+msgstr "E86: Le tampon %<PRId64> n'existe pas"
+
#: ../globals.h:1108
msgid "E682: Invalid search pattern or delimiter"
msgstr "E682: Délimiteur ou motif de recherche invalide"
@@ -4308,7 +4322,7 @@ msgid ""
" to recover the changes (see \":help recovery\").\n"
msgstr ""
"\"\n"
-" pour récupérer le fichier (voir \":help recovery\").\n"
+" pour récupérer le fichier (consultez \":help recovery\").\n"
#: ../memline.c:3250
msgid " If you did this already, delete the swap file \""
@@ -4805,6 +4819,11 @@ msgstr "E522: Introuvable dans termcap"
msgid "E539: Illegal character <%s>"
msgstr "E539: Caractère <%s> invalide"
+#: ../option.c:2253
+#, c-format
+msgid "For option %s"
+msgstr "Pour l'option %s"
+
#: ../option.c:3862
msgid "E529: Cannot set 'term' to empty string"
msgstr "E529: 'term' ne doit pas être une chaîne vide"
@@ -6779,9 +6798,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "Reading from stdin..."
#~ msgstr "Lecture de stdin..."
-#~ msgid "[blowfish]"
-#~ msgstr "[blowfish]"
-
#~ msgid "[crypted]"
#~ msgstr "[chiffré]"
@@ -6909,8 +6925,8 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgstr ""
#~ "Vim : Une requête \"die\" a été reçue par le gestionnaire de session\n"
-#~ msgid "Close"
-#~ msgstr "Fermer"
+#~ msgid "Close tab"
+#~ msgstr "Fermer l'onglet"
#~ msgid "New tab"
#~ msgstr "Nouvel onglet"
@@ -6968,13 +6984,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "E672: Unable to open window inside MDI application"
#~ msgstr "E672: Impossible d'ouvrir une fenêtre dans une application MDI"
-# DB - Les quelques messages qui suivent se retrouvent aussi ici :
-# gui_gtk_x11.c:3170 et suivants.
-# Les libellés changent un peu (majuscule par exemple).
-# La VF tâche de les unifier.
-#~ msgid "Close tab"
-#~ msgstr "Fermer l'onglet"
-
#~ msgid "Open tab..."
#~ msgstr "Ouvrir dans un onglet..."
@@ -7148,9 +7157,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "E836: This Vim cannot execute :python after using :py3"
#~ msgstr "E836: Vim ne peut pas exécuter :python après avoir utilisé :py3"
-#~ msgid "only string keys are allowed"
-#~ msgstr "seule une chaine est autorisée comme clé"
-
#~ msgid ""
#~ "E263: Sorry, this command is disabled, the Python library could not be "
#~ "loaded."
@@ -7684,12 +7690,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "E338: Sorry, no file browser in console mode"
#~ msgstr "E338: Désolé, pas de sélecteur de fichiers en mode console"
-#~ msgid "Vim: preserving files...\n"
-#~ msgstr "Vim : préservation des fichiers...\n"
-
-#~ msgid "Vim: Finished.\n"
-#~ msgstr "Vim : Fini.\n"
-
#~ msgid "ERROR: "
#~ msgstr "ERREUR : "
@@ -7718,6 +7718,10 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "E547: Illegal mouseshape"
#~ msgstr "E547: Forme de curseur invalide"
+#~ msgid "Warning: Using a weak encryption method; see :help 'cm'"
+#~ msgstr ""
+#~ "Alerte : utilisation d'une méthode de chiffrage faible ; consultez :help 'cm'"
+
#~ msgid "Enter encryption key: "
#~ msgstr "Tapez la clé de chiffrement : "
@@ -7861,15 +7865,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "E245: Illegal char '%c' in font name \"%s\""
#~ msgstr "E245: Caractère '%c' invalide dans le nom de fonte \"%s\""
-#~ msgid "Vim: Double signal, exiting\n"
-#~ msgstr "Vim : Double signal, sortie\n"
-
-#~ msgid "Vim: Caught deadly signal %s\n"
-#~ msgstr "Vim : Signal mortel %s intercepté\n"
-
-#~ msgid "Vim: Caught deadly signal\n"
-#~ msgstr "Vim : Signal mortel intercepté\n"
-
#~ msgid "Opening the X display took %<PRId64> msec"
#~ msgstr "L'ouverture du display X a pris %<PRId64> ms"
@@ -7970,7 +7965,7 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgstr ""
#~ "VIMRUN.EXE est introuvable votre $PATH.\n"
#~ "Les commandes externes ne feront pas de pause une fois terminées.\n"
-#~ "Voir :help win32-vimrun pour plus d'informations."
+#~ "Consultez :help win32-vimrun pour plus d'informations."
#~ msgid "Vim Warning"
#~ msgstr "Alerte Vim"
@@ -7982,11 +7977,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgstr ""
#~ "E868: Erreur lors de la construction du NFA avec classe d'équivalence"
-#~ msgid "E999: (NFA regexp internal error) Should not process NOT node !"
-#~ msgstr ""
-#~ "E999: (erreur interne du regexp NFA) Un noeud 'NOT' ne devrait pas être "
-#~ "traité !"
-
#~ msgid "E878: (NFA) Could not allocate memory for branch traversal!"
#~ msgstr ""
#~ "E878: (NFA) Impossible d'allouer la mémoire pour parcourir les branches!"
@@ -8306,30 +8296,18 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "can't delete OutputObject attributes"
#~ msgstr "impossible d'effacer les attributs d'OutputObject"
-#~ msgid "softspace must be an integer"
-#~ msgstr "softspace doit être un nombre entier"
-
#~ msgid "invalid attribute"
#~ msgstr "attribut invalide"
-#~ msgid "writelines() requires list of strings"
-#~ msgstr "writelines() requiert une liste de chaînes"
-
#~ msgid "E264: Python: Error initialising I/O objects"
#~ msgstr "E264: Python : Erreur d'initialisation des objets d'E/S"
#~ msgid "empty keys are not allowed"
#~ msgstr "les clés vides ne sont pas autorisées"
-#~ msgid "Cannot delete DictionaryObject attributes"
-#~ msgstr "Impossible d'effacer les attributs de DictionaryObject"
-
#~ msgid "Cannot modify fixed dictionary"
#~ msgstr "Impossible de modifier un dictionnaire fixe"
-#~ msgid "Cannot set this attribute"
-#~ msgstr "Impossible d'initialiser cet attribut"
-
#~ msgid "dict is locked"
#~ msgstr "dictionnaire est verrouillé"
@@ -8348,15 +8326,9 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "Failed to add item to list"
#~ msgstr "Ajout à la liste a échoué"
-#~ msgid "can only assign lists to slice"
-#~ msgstr "seules des tranches peuvent être assignées aux listes"
-
#~ msgid "internal error: failed to add item to list"
#~ msgstr "erreur interne : ajout d'élément à la liste a échoué"
-#~ msgid "can only concatenate with lists"
-#~ msgstr "on ne peut que concaténer avec des listes"
-
#~ msgid "cannot delete vim.dictionary attributes"
#~ msgstr "impossible d'effacer les attributs de vim.dictionary"
@@ -8366,9 +8338,6 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "cannot set this attribute"
#~ msgstr "impossible d'initialiser cet attribut"
-#~ msgid "'self' argument must be a dictionary"
-#~ msgstr "l'argument 'self' doit être un dictionnaire"
-
#~ msgid "failed to run function"
#~ msgstr "exécution de la fonction a échoué"
@@ -8378,24 +8347,9 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "unable to unset option without global value"
#~ msgstr "impossible de désactiver une option sans une valeur globale"
-#~ msgid "object must be integer"
-#~ msgstr "objet doit être un nombre entier"
-
-#~ msgid "object must be string"
-#~ msgstr "objet doit être de type string"
-
#~ msgid "attempt to refer to deleted tab page"
#~ msgstr "tentative de référencer un onglet effacé"
-#~ msgid "<tabpage object (deleted) at %p>"
-#~ msgstr "<objet onglet (effacé) à %p>"
-
-#~ msgid "<tabpage object (unknown) at %p>"
-#~ msgstr "<objet onglet (inconnu) à %p>"
-
-#~ msgid "<tabpage %d>"
-#~ msgstr "<onglet %d>"
-
#~ msgid "no such tab page"
#~ msgstr "cet onglet n'existe pas"
@@ -8408,27 +8362,12 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "cursor position outside buffer"
#~ msgstr "curseur positionné en dehors du tampon"
-#~ msgid "<window object (deleted) at %p>"
-#~ msgstr "<objet fenêtre (effacé) à %p>"
-
-#~ msgid "<window object (unknown) at %p>"
-#~ msgstr "<objet fenêtre (inconnu) à %p>"
-
-#~ msgid "<window %d>"
-#~ msgstr "<fenêtre %d>"
-
#~ msgid "no such window"
#~ msgstr "Cette fenêtre n'existe pas"
#~ msgid "attempt to refer to deleted buffer"
#~ msgstr "tentative de référencer un tampon effacé"
-#~ msgid "<buffer object (deleted) at %p>"
-#~ msgstr "<objet tampon (effacé) à %p>"
-
-#~ msgid "key must be integer"
-#~ msgstr "la clé doit être un nombre entier"
-
#~ msgid "expected vim.buffer object"
#~ msgstr "objet vim.buffer attendu"
@@ -8467,18 +8406,3 @@ msgstr "E446: Aucun nom de fichier sous le curseur"
#~ msgid "internal error: invalid value type"
#~ msgstr "erreur interne : type de valeur invalide"
-
-#~ msgid "E860: Eval did not return a valid python 3 object"
-#~ msgstr "E860: Eval n'a pas retourné un object python 3 valid"
-
-#~ msgid "E861: Failed to convert returned python 3 object to vim value"
-#~ msgstr "E861: Conversion d'objet python 3 à une valeur de vim a échoué"
-
-#~ msgid "E863: return value must be an instance of str"
-#~ msgstr "E863: valeur de retour doit être une instance de Str"
-
-#~ msgid "Only boolean objects are allowed"
-#~ msgstr "Seuls les objets booléens sont autorisés"
-
-#~ msgid "no such key in dictionary"
-#~ msgstr "cette clé est inexistante dans le dictionnaire"
diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po
index 729697eee3..171e155689 100644
--- a/src/nvim/po/it.po
+++ b/src/nvim/po/it.po
@@ -11,10 +11,10 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: vim 7.4b\n"
+"Project-Id-Version: vim 7.4\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-05-26 14:21+0200\n"
-"PO-Revision-Date: 2013-08-03 17:14+0200\n"
+"POT-Creation-Date: 2015-08-11 20:58+0200\n"
+"PO-Revision-Date: 2015-08-11 22:02+0200\n"
"Last-Translator: Vlad Sandrini <vlad.gently@gmail.com>\n"
"Language-Team: Italian Antonio Colombo <azc100@gmail."
"com> Vlad Sandrini <vlad.gently@gmail."
@@ -104,11 +104,6 @@ msgstr "E84: Nessun buffer risulta modificato"
msgid "E85: There is no listed buffer"
msgstr "E85: Non c'è alcun buffer elencato"
-#: ../buffer.c:913
-#, c-format
-msgid "E86: Buffer %<PRId64> does not exist"
-msgstr "E86: Non esiste il buffer %<PRId64>"
-
#: ../buffer.c:915
msgid "E87: Cannot go beyond last buffer"
msgstr "E87: Non posso oltrepassare l'ultimo buffer"
@@ -128,7 +123,7 @@ msgstr ""
#. wrap around (may cause duplicates)
#: ../buffer.c:1423
msgid "W14: Warning: List of file names overflow"
-msgstr "W14: Attenzione: Superato limite della lista dei nomi di file"
+msgstr "W14: Avviso: Superato limite della lista dei nomi di file"
#: ../buffer.c:1555 ../quickfix.c:3361
#, c-format
@@ -148,7 +143,7 @@ msgstr "E94: Nessun buffer corrispondente a %s"
#: ../buffer.c:2161
#, c-format
msgid "line %<PRId64>"
-msgstr "linea %<PRId64>"
+msgstr "riga %<PRId64>"
#: ../buffer.c:2233
msgid "E95: Buffer with this name already exists"
@@ -181,17 +176,17 @@ msgstr "[in sola lettura]"
#: ../buffer.c:2524
#, c-format
msgid "1 line --%d%%--"
-msgstr "1 linea --%d%%--"
+msgstr "1 riga --%d%%--"
#: ../buffer.c:2526
#, c-format
msgid "%<PRId64> lines --%d%%--"
-msgstr "%<PRId64> linee --%d%%--"
+msgstr "%<PRId64> righe --%d%%--"
#: ../buffer.c:2530
#, c-format
msgid "line %<PRId64> of %<PRId64> --%d%%-- col "
-msgstr "linea %<PRId64> di %<PRId64> --%d%%-- col "
+msgstr "riga %<PRId64> di %<PRId64> --%d%%-- col "
#: ../buffer.c:2632 ../buffer.c:4292 ../memline.c:1554
msgid "[No Name]"
@@ -250,7 +245,7 @@ msgstr "Segni per %s:"
#: ../buffer.c:4543
#, c-format
msgid " line=%<PRId64> id=%d name=%s"
-msgstr " linea=%<PRId64> id=%d, nome=%s"
+msgstr " riga=%<PRId64> id=%d, nome=%s"
#: ../cursor_shape.c:68
msgid "E545: Missing colon"
@@ -346,11 +341,11 @@ msgstr " modalità ^X (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
#: ../edit.c:85
msgid " Whole line completion (^L^N^P)"
-msgstr " Completamento Linea Intera (^L^N^P)"
+msgstr " Completamento riga intera (^L^N^P)"
#: ../edit.c:86
msgid " File name completion (^F^N^P)"
-msgstr " Completamento nomi File (^F^N^P)"
+msgstr " Completamento nomi file (^F^N^P)"
#: ../edit.c:87
msgid " Tag completion (^]^N^P)"
@@ -374,7 +369,7 @@ msgstr " Completamento Thesaurus (^T^N^P)"
#: ../edit.c:93
msgid " Command-line completion (^V^N^P)"
-msgstr " Completamento linea comandi (^V^N^P)"
+msgstr " Completamento riga comandi (^V^N^P)"
#: ../edit.c:94
msgid " User defined completion (^U^N^P)"
@@ -452,7 +447,7 @@ msgstr "Ritorno all'originale"
#: ../edit.c:4621
msgid "Word from other line"
-msgstr "Parola da un'altra linea"
+msgstr "Parola da un'altra riga"
#: ../edit.c:4624
msgid "The only match"
@@ -680,6 +675,11 @@ msgstr "E696: Manca virgola nella Lista: %s"
msgid "E697: Missing end of List ']': %s"
msgstr "E697: Manca ']' a fine Lista: %s"
+#: ../eval.c:5750
+#, c-format
+msgid "Not enough memory to set references, garbage collection aborted!"
+msgstr "Memoria insufficiente per impostarlo, recupero memoria fallito!"
+
#: ../eval.c:6475
#, c-format
msgid "E720: Missing colon in Dictionary: %s"
@@ -754,15 +754,15 @@ msgstr "E785: complete() può essere usata solo in modalità inserimento"
msgid "&Ok"
msgstr "&OK"
-#: ../eval.c:8676
-#, c-format
-msgid "E737: Key already exists: %s"
-msgstr "E737: Chiave già esistente: %s"
-
#: ../eval.c:8692
msgid "extend() argument"
msgstr "argomento di extend()"
+#: ../eval.c:9345
+#, c-format
+msgid "E737: Key already exists: %s"
+msgstr "E737: Chiave già esistente: %s"
+
#: ../eval.c:8915
msgid "map() argument"
msgstr "argomento di map()"
@@ -774,7 +774,7 @@ msgstr "argomento di filter()"
#: ../eval.c:9229
#, c-format
msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld linee: "
+msgstr "+-%s%3ld righe: "
#: ../eval.c:9291
#, c-format
@@ -1043,21 +1043,21 @@ msgstr "> %d, Esa %08x, Ottale %o"
#: ../ex_cmds.c:684
msgid "E134: Move lines into themselves"
-msgstr "E134: Movimento di linee verso se stesse"
+msgstr "E134: Movimento di righe verso se stesse"
#: ../ex_cmds.c:747
msgid "1 line moved"
-msgstr "1 linea mossa"
+msgstr "1 riga mossa"
#: ../ex_cmds.c:749
#, c-format
msgid "%<PRId64> lines moved"
-msgstr "%<PRId64> linee mosse"
+msgstr "%<PRId64> righe mosse"
#: ../ex_cmds.c:1175
#, c-format
msgid "%<PRId64> lines filtered"
-msgstr "%<PRId64> linee filtrate"
+msgstr "%<PRId64> righe filtrate"
#: ../ex_cmds.c:1194
msgid "E135: *Filter* Autocommands must not change current buffer"
@@ -1070,7 +1070,7 @@ msgstr "[Non salvato dopo l'ultima modifica]\n"
#: ../ex_cmds.c:1424
#, c-format
msgid "%sviminfo: %s in line: "
-msgstr "%sviminfo: %s nella linea: "
+msgstr "%sviminfo: %s nella riga: "
#: ../ex_cmds.c:1431
msgid "E136: viminfo: Too many errors, skipping rest of file"
@@ -1239,12 +1239,12 @@ msgstr "%<PRId64> sostituzioni"
#: ../ex_cmds.c:4392
msgid " on 1 line"
-msgstr " in 1 linea"
+msgstr " in 1 riga"
#: ../ex_cmds.c:4395
#, c-format
msgid " on %<PRId64> lines"
-msgstr " in %<PRId64> linee"
+msgstr " in %<PRId64> righe"
#: ../ex_cmds.c:4438
msgid "E147: Cannot do :global recursive"
@@ -1257,7 +1257,7 @@ msgstr "E148: Manca espressione regolare nel comando 'global'"
#: ../ex_cmds.c:4508
#, c-format
msgid "Pattern found in every line: %s"
-msgstr "Espressione trovata su ogni linea: %s"
+msgstr "Espressione trovata su ogni riga: %s"
#: ../ex_cmds.c:4510
#, c-format
@@ -1355,6 +1355,11 @@ msgstr "E158: Nome buffer non valido: %s"
msgid "E157: Invalid sign ID: %<PRId64>"
msgstr "E157: ID 'sign' non valido: %<PRId64>"
+#: ../ex_cmds.c:5517
+#, c-format
+msgid "E885: Not possible to change sign %s"
+msgstr "E885: Impossibile cambiare segno %s"
+
#: ../ex_cmds.c:6066
msgid " (not supported)"
msgstr " (non supportata)"
@@ -1370,7 +1375,7 @@ msgstr "Entro modalità Debug. Batti \"cont\" per continuare."
#: ../ex_cmds2.c:143 ../ex_docmd.c:759
#, c-format
msgid "line %<PRId64>: %s"
-msgstr "linea %<PRId64>: %s"
+msgstr "riga %<PRId64>: %s"
#: ../ex_cmds2.c:145
#, c-format
@@ -1380,7 +1385,7 @@ msgstr "com: %s"
#: ../ex_cmds2.c:322
#, c-format
msgid "Breakpoint in \"%s%s\" line %<PRId64>"
-msgstr "Pausa in \"%s%s\" linea %<PRId64>"
+msgstr "Pausa in \"%s%s\" riga %<PRId64>"
#: ../ex_cmds2.c:581
#, c-format
@@ -1394,7 +1399,7 @@ msgstr "Nessun 'breakpoint' definito"
#: ../ex_cmds2.c:617
#, c-format
msgid "%3d %s %s line %<PRId64>"
-msgstr "%3d %s %s linea %<PRId64>"
+msgstr "%3d %s %s riga %<PRId64>"
#: ../ex_cmds2.c:942
msgid "E750: First use \":profile start {fname}\""
@@ -1417,7 +1422,7 @@ msgstr "E162: Buffer \"%s\" non salvato dopo modifica"
#: ../ex_cmds2.c:1480
msgid "Warning: Entered other buffer unexpectedly (check autocommands)"
msgstr ""
-"Attenzione: Entrato in altro buffer inaspettatamente (controllare "
+"Avviso: Entrato in altro buffer inaspettatamente (controllare "
"autocomandi)"
#: ../ex_cmds2.c:1826
@@ -1465,7 +1470,7 @@ msgstr "non riesco ad eseguire \"%s\""
#: ../ex_cmds2.c:2520
#, c-format
msgid "line %<PRId64>: could not source \"%s\""
-msgstr "linea %<PRId64>: non riesco ad eseguire \"%s\""
+msgstr "riga %<PRId64>: non riesco ad eseguire \"%s\""
#: ../ex_cmds2.c:2535
#, c-format
@@ -1475,7 +1480,7 @@ msgstr "eseguo \"%s\""
#: ../ex_cmds2.c:2537
#, c-format
msgid "line %<PRId64>: sourcing \"%s\""
-msgstr "linea %<PRId64>: eseguo \"%s\""
+msgstr "riga %<PRId64>: eseguo \"%s\""
#: ../ex_cmds2.c:2693
#, c-format
@@ -1504,7 +1509,7 @@ msgstr "gestore di errore"
#: ../ex_cmds2.c:3020
msgid "W15: Warning: Wrong line separator, ^M may be missing"
-msgstr "W15: Attenzione: Separatore di linea errato, forse manca ^M"
+msgstr "W15: Avviso: Separatore di riga errato, forse manca ^M"
#: ../ex_cmds2.c:3139
msgid "E167: :scriptencoding used outside of a sourced file"
@@ -1606,10 +1611,10 @@ msgstr "E174: Il comando esiste già: aggiungi ! per sostituirlo"
#: ../ex_docmd.c:4432
msgid ""
"\n"
-" Name Args Range Complete Definition"
+" Name Args Address Complete Definition"
msgstr ""
"\n"
-" Nome Arg. Inter Completo Definizione"
+" Nome Arg. Indir. Completo Definizione"
#: ../ex_docmd.c:4516
msgid "No user-defined commands found"
@@ -1635,6 +1640,10 @@ msgstr "E178: Valore predefinito del contatore non valido"
msgid "E179: argument required for -complete"
msgstr "E179: argomento necessario per -complete"
+#: ../ex_docmd.c:4923
+msgid "E179: argument required for -addr"
+msgstr "E179: argomento necessario per -addr"
+
#: ../ex_docmd.c:4635
#, c-format
msgid "E181: Invalid attribute: %s"
@@ -1658,6 +1667,11 @@ msgstr "E841: Nome riservato, non usabile in un comando definito dall'utente"
msgid "E184: No such user-defined command: %s"
msgstr "E184: Comando definito dall'utente %s inesistente"
+#: ../ex_docmd.c:5516
+#, c-format
+msgid "E180: Invalid address type value: %s"
+msgstr "E180: Tipo di indirizzo non valido: %s"
+
#: ../ex_docmd.c:5219
#, c-format
msgid "E180: Invalid complete value: %s"
@@ -1666,7 +1680,7 @@ msgstr "E180: Valore %s non valido per 'complete'"
#: ../ex_docmd.c:5225
msgid "E468: Completion argument only allowed for custom completion"
msgstr ""
-"E468: Argomento di completamento permesso solo per completamento "
+"E468: Argomento di completamento consentito solo per completamento "
"personalizzato"
#: ../ex_docmd.c:5231
@@ -1815,7 +1829,7 @@ msgstr "Eccezione scartata: %s"
#: ../ex_eval.c:588 ../ex_eval.c:634
#, c-format
msgid "%s, line %<PRId64>"
-msgstr "%s, linea %<PRId64>"
+msgstr "%s, riga %<PRId64>"
#. always scroll up, don't overwrite
#: ../ex_eval.c:608
@@ -1961,7 +1975,7 @@ msgstr ""
#: ../ex_getln.c:5047
msgid "Command Line"
-msgstr "Linea di Comando"
+msgstr "Riga di Comando"
#: ../ex_getln.c:5048
msgid "Search String"
@@ -1973,7 +1987,7 @@ msgstr "Espressione"
#: ../ex_getln.c:5050
msgid "Input Line"
-msgstr "Linea di Input"
+msgstr "Riga di Input"
#: ../ex_getln.c:5117
msgid "E198: cmd_pchar beyond the command length"
@@ -2091,7 +2105,7 @@ msgstr "[manca CR]"
#: ../fileio.c:1819
msgid "[long lines split]"
-msgstr "[linee lunghe divise]"
+msgstr "[righe lunghe divise]"
#: ../fileio.c:1823 ../fileio.c:3512
msgid "[NOT converted]"
@@ -2104,12 +2118,12 @@ msgstr "[convertito]"
#: ../fileio.c:1831
#, c-format
msgid "[CONVERSION ERROR in line %<PRId64>]"
-msgstr "[ERRORE DI CONVERSIONE alla linea %<PRId64>]"
+msgstr "[ERRORE DI CONVERSIONE alla riga %<PRId64>]"
#: ../fileio.c:1835
#, c-format
msgid "[ILLEGAL BYTE in line %<PRId64>]"
-msgstr "[BYTE NON VALIDO alla linea %<PRId64>]"
+msgstr "[BYTE NON VALIDO alla riga %<PRId64>]"
#: ../fileio.c:1838
msgid "[READ ERRORS]"
@@ -2137,7 +2151,7 @@ msgstr "E203: Buffer in scrittura cancellato o scaricato dagli autocomandi"
#: ../fileio.c:2486
msgid "E204: Autocommand changed number of lines in unexpected way"
-msgstr "E204: L'autocomando ha modificato numero linee in maniera imprevista"
+msgstr "E204: L'autocomando ha modificato numero righe in maniera imprevista"
#: ../fileio.c:2548 ../fileio.c:2565
msgid "is not a file or writable device"
@@ -2213,7 +2227,7 @@ msgid ""
"E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty to "
"override)"
msgstr ""
-"E513: errore in scrittura, conversione fallita alla linea %<PRId64> (rendere "
+"E513: errore in scrittura, conversione fallita alla riga %<PRId64> (rendere "
"'fenc' nullo per eseguire comunque)"
#: ../fileio.c:3448
@@ -2227,7 +2241,7 @@ msgstr " ERRORE DI CONVERSIONE"
#: ../fileio.c:3509
#, c-format
msgid " in line %<PRId64>;"
-msgstr " alla linea %<PRId64>;"
+msgstr " alla riga %<PRId64>;"
#: ../fileio.c:3519
msgid "[Device]"
@@ -2271,7 +2285,7 @@ msgid ""
"WARNING: Original file may be lost or damaged\n"
msgstr ""
"\n"
-"ATTENZIONE: Il file originale può essere perso o danneggiato\n"
+"AVVISO: Il file originale può essere perso o danneggiato\n"
#: ../fileio.c:3675
msgid "don't quit the editor until the file is successfully written!"
@@ -2303,12 +2317,12 @@ msgstr "[in formato UNIX]"
#: ../fileio.c:3831
msgid "1 line, "
-msgstr "1 linea, "
+msgstr "1 riga, "
#: ../fileio.c:3833
#, c-format
msgid "%<PRId64> lines, "
-msgstr "%<PRId64> linee,"
+msgstr "%<PRId64> righe,"
#: ../fileio.c:3836
msgid "1 character"
@@ -2325,14 +2339,14 @@ msgstr "[noeol]"
#: ../fileio.c:3849
msgid "[Incomplete last line]"
-msgstr "[Manca carattere di fine linea]"
+msgstr "[Manca carattere di fine riga]"
#. don't overwrite messages here
#. must give this prompt
#. don't use emsg() here, don't want to flush the buffers
#: ../fileio.c:3865
msgid "WARNING: The file has been changed since reading it!!!"
-msgstr "ATTENZIONE: File modificato dopo essere stato letto dall'Editor!!!"
+msgstr "AVVISO: File modificato dopo essere stato letto dall'Editor!!!"
#: ../fileio.c:3867
msgid "Do you really want to write to it"
@@ -2368,7 +2382,7 @@ msgid ""
"W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as "
"well"
msgstr ""
-"W12: Attenzione: File \"%s\" modificato su disco ed anche nel buffer di Vim"
+"W12: Avviso: File \"%s\" modificato su disco ed anche nel buffer di Vim"
#: ../fileio.c:4907
msgid "See \":help W12\" for more info."
@@ -2377,7 +2391,7 @@ msgstr "Vedere \":help W12\" per ulteriori informazioni."
#: ../fileio.c:4910
#, c-format
msgid "W11: Warning: File \"%s\" has changed since editing started"
-msgstr "W11: Attenzione: File \"%s\" modificato dopo l'apertura"
+msgstr "W11: Avviso: File \"%s\" modificato dopo l'apertura"
#: ../fileio.c:4911
msgid "See \":help W11\" for more info."
@@ -2386,7 +2400,7 @@ msgstr "Vedere \":help W11\" per ulteriori informazioni."
#: ../fileio.c:4914
#, c-format
msgid "W16: Warning: Mode of file \"%s\" has changed since editing started"
-msgstr "W16: Attenzione: Modo File \"%s\" modificato dopo l'apertura"
+msgstr "W16: Avviso: Modo File \"%s\" modificato dopo l'apertura"
#: ../fileio.c:4915
msgid "See \":help W16\" for more info."
@@ -2395,11 +2409,11 @@ msgstr "Vedere \":help W16\" per ulteriori informazioni."
#: ../fileio.c:4927
#, c-format
msgid "W13: Warning: File \"%s\" has been created after editing started"
-msgstr "W13: Attenzione: Il file \"%s\" risulta creato dopo l'apertura"
+msgstr "W13: Avviso: Il file \"%s\" risulta creato dopo l'apertura"
#: ../fileio.c:4947
msgid "Warning"
-msgstr "Attenzione"
+msgstr "Avviso"
#: ../fileio.c:4948
msgid ""
@@ -2513,7 +2527,7 @@ msgstr "E351: Non posso cancellare piegatura con il 'foldmethod' in uso"
#: ../fold.c:1784
#, c-format
msgid "+--%3ld lines folded "
-msgstr "+--%3ld linee piegate"
+msgstr "+--%3ld righe piegate"
#. buffer has already been read
#: ../getchar.c:273
@@ -2679,7 +2693,7 @@ msgstr "E364: Chiamata a libreria fallita per \"%s()\""
#: ../globals.h:1026
msgid "E19: Mark has invalid line number"
-msgstr "E19: 'Mark' con numero linea non valido"
+msgstr "E19: 'Mark' con numero riga non valido"
#: ../globals.h:1027
msgid "E20: Mark not set"
@@ -2720,7 +2734,7 @@ msgstr "E29: Ancora nessun testo inserito"
#: ../globals.h:1038
msgid "E30: No previous command line"
-msgstr "E30: Nessuna linea comandi precedente"
+msgstr "E30: Nessuna riga comandi precedente"
#: ../globals.h:1039
msgid "E31: No such mapping"
@@ -2947,6 +2961,11 @@ msgstr "E363: l'espressione usa troppa memoria rispetto a 'maxmempattern'"
msgid "E749: empty buffer"
msgstr "E749: buffer vuoto"
+#: ../globals.h:1226
+#, c-format
+msgid "E86: Buffer %<PRId64> does not exist"
+msgstr "E86: Non esiste il buffer %<PRId64>"
+
#: ../globals.h:1108
msgid "E682: Invalid search pattern or delimiter"
msgstr "E682: Espressione o delimitatore di ricerca non validi"
@@ -3260,11 +3279,11 @@ msgid ""
" # line"
msgstr ""
"\n"
-" # linea"
+" # riga"
#: ../if_cscope.c:1713
msgid "filename / context / line\n"
-msgstr "nomefile / contest / linea\n"
+msgstr "nomefile / contest / riga\n"
#: ../if_cscope.c:1809
#, c-format
@@ -3326,16 +3345,16 @@ msgstr "Non posso aprire come script output: \""
#: ../main.c:1622
msgid "Vim: Warning: Output is not to a terminal\n"
-msgstr "Vim: Attenzione: Output non diretto a un terminale\n"
+msgstr "Vim: Avviso: Output non diretto a un terminale\n"
#: ../main.c:1624
msgid "Vim: Warning: Input is not from a terminal\n"
-msgstr "Vim: Attenzione: Input non proveniente da un terminale\n"
+msgstr "Vim: Avviso: Input non proveniente da un terminale\n"
#. just in case..
#: ../main.c:1891
msgid "pre-vimrc command line"
-msgstr "linea comandi prima di vimrc"
+msgstr "riga comandi prima di vimrc"
#: ../main.c:1964
#, c-format
@@ -3528,7 +3547,7 @@ msgstr "+\t\t\tPosizionati alla fine del file"
#: ../main.c:2231
msgid "+<lnum>\t\tStart at line <lnum>"
-msgstr "+<lnum>\t\tPosizionati alla linea <lnum>"
+msgstr "+<lnum>\t\tPosizionati alla riga <lnum>"
#: ../main.c:2232
msgid "--cmd <command>\tExecute <command> before loading any vimrc file"
@@ -3589,7 +3608,7 @@ msgid ""
"mark line col file/text"
msgstr ""
"\n"
-"mark linea col.file/testo"
+"mark riga col.file/testo"
#. Highlight title
#: ../mark.c:789
@@ -3598,7 +3617,7 @@ msgid ""
" jump line col file/text"
msgstr ""
"\n"
-" salt.linea col.file/testo"
+" salt.riga col.file/testo"
#. Highlight title
#: ../mark.c:831
@@ -3607,7 +3626,7 @@ msgid ""
"change line col text"
msgstr ""
"\n"
-"modif linea col testo"
+"modif riga col testo"
#: ../mark.c:1238
msgid ""
@@ -3767,7 +3786,7 @@ msgstr "File originale \"%s\""
#: ../memline.c:995
msgid "E308: Warning: Original file may have been changed"
msgstr ""
-"E308: Attenzione: il file originale può essere stato modificato nel frattempo"
+"E308: Avviso: il file originale può essere stato modificato nel frattempo"
#: ../memline.c:1061
#, c-format
@@ -3776,7 +3795,7 @@ msgstr "E309: Impossibile leggere blocco 1 da %s"
#: ../memline.c:1065
msgid "???MANY LINES MISSING"
-msgstr "???MOLTE LINEE MANCANTI"
+msgstr "???MOLTE RIGHE MANCANTI"
#: ../memline.c:1076
msgid "???LINE COUNT WRONG"
@@ -3788,7 +3807,7 @@ msgstr "???BLOCCO VUOTO"
#: ../memline.c:1103
msgid "???LINES MISSING"
-msgstr "???LINEE MANCANTI"
+msgstr "???RIGHE MANCANTI"
#: ../memline.c:1128
#, c-format
@@ -3801,12 +3820,12 @@ msgstr "???BLOCCO MANCANTE"
#: ../memline.c:1147
msgid "??? from here until ???END lines may be messed up"
-msgstr "??? da qui fino a ???END le linee possono essere fuori ordine"
+msgstr "??? da qui fino a ???END le righe possono essere fuori ordine"
#: ../memline.c:1164
msgid "??? from here until ???END lines may have been inserted/deleted"
msgstr ""
-"??? da qui fino a ???END linee possono essere state inserite/cancellate"
+"??? da qui fino a ???END righe possono essere state inserite/cancellate"
#: ../memline.c:1181
msgid "???END"
@@ -3819,7 +3838,7 @@ msgstr "E311: Recupero Interrotto"
#: ../memline.c:1243
msgid ""
"E312: Errors detected while recovering; look for lines starting with ???"
-msgstr "E312: Errori durante recupero; controlla linee che iniziano con ???"
+msgstr "E312: Errori durante recupero; controlla righe che iniziano con ???"
#: ../memline.c:1245
msgid "See \":help E312\" for more information."
@@ -3980,12 +3999,12 @@ msgstr "E314: Preservazione fallita"
#: ../memline.c:1819
#, c-format
msgid "E315: ml_get: invalid lnum: %<PRId64>"
-msgstr "E315: ml_get: numero linea non valido: %<PRId64>"
+msgstr "E315: ml_get: numero riga non valido: %<PRId64>"
#: ../memline.c:1851
#, c-format
msgid "E316: ml_get: cannot find line %<PRId64>"
-msgstr "E316: ml_get: non riesco a trovare la linea %<PRId64>"
+msgstr "E316: ml_get: non riesco a trovare la riga %<PRId64>"
#: ../memline.c:2236
msgid "E317: pointer block id wrong 3"
@@ -4010,7 +4029,7 @@ msgstr "cancellato blocco 1?"
#: ../memline.c:2707
#, c-format
msgid "E320: Cannot find line %<PRId64>"
-msgstr "E320: Non riesco a trovare la linea %<PRId64>"
+msgstr "E320: Non riesco a trovare la riga %<PRId64>"
#: ../memline.c:2916
msgid "E317: pointer block id wrong"
@@ -4023,12 +4042,12 @@ msgstr "pe_line_count a zero"
#: ../memline.c:2955
#, c-format
msgid "E322: line number out of range: %<PRId64> past the end"
-msgstr "E322: numero linea non ammissibile: %<PRId64> dopo la fine"
+msgstr "E322: numero riga non ammissibile: %<PRId64> dopo la fine"
#: ../memline.c:2959
#, c-format
msgid "E323: line count wrong in block %<PRId64>"
-msgstr "E323: contatore linee errato nel blocco %<PRId64>"
+msgstr "E323: contatore righe errato nel blocco %<PRId64>"
#: ../memline.c:2999
msgid "Stack size increases"
@@ -4242,7 +4261,7 @@ msgstr "Errore/i eseguendo %s:"
#: ../message.c:445
#, c-format
msgid "line %4ld:"
-msgstr "linea %4ld:"
+msgstr "riga %4ld:"
#: ../message.c:617
#, c-format
@@ -4264,7 +4283,7 @@ msgstr "Premi INVIO o un comando per proseguire"
#: ../message.c:1843
#, c-format
msgid "%s line %<PRId64>"
-msgstr "%s linea %<PRId64>"
+msgstr "%s riga %<PRId64>"
#: ../message.c:2392
msgid "-- More --"
@@ -4272,7 +4291,7 @@ msgstr "-- Ancora --"
#: ../message.c:2398
msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "
-msgstr " SPAZIO/d/j: schermo/pagina/linea giù, b/u/k: su, q: abbandona "
+msgstr " SPAZIO/d/j: schermo/pagina/riga giù, b/u/k: su, q: abbandona "
#: ../message.c:3021 ../message.c:3031
msgid "Question"
@@ -4324,7 +4343,7 @@ msgstr "E767: Troppi argomenti per printf()"
#: ../misc1.c:2256
msgid "W10: Warning: Changing a readonly file"
-msgstr "W10: Attenzione: Modifica a un file in sola-lettura"
+msgstr "W10: Avviso: Modifica a un file in sola-lettura"
#: ../misc1.c:2537
msgid "Type number and <Enter> or click with mouse (empty cancels): "
@@ -4337,21 +4356,21 @@ msgstr "Inserire numero e <Invio> (vuoto per annullare): "
#: ../misc1.c:2585
msgid "1 more line"
-msgstr "1 linea in più"
+msgstr "1 riga in più"
#: ../misc1.c:2588
msgid "1 line less"
-msgstr "1 linea in meno"
+msgstr "1 riga in meno"
#: ../misc1.c:2593
#, c-format
msgid "%<PRId64> more lines"
-msgstr "%<PRId64> linee in più"
+msgstr "%<PRId64> righe in più"
#: ../misc1.c:2596
#, c-format
msgid "%<PRId64> fewer lines"
-msgstr "%<PRId64> linee in meno"
+msgstr "%<PRId64> righe in meno"
#: ../misc1.c:2599
msgid " (Interrupted)"
@@ -4376,7 +4395,7 @@ msgstr "E774: opzione 'operatorfunc' non impostata"
#: ../normal.c:2637
msgid "Warning: terminal cannot highlight"
-msgstr "Attenzione: il terminale non è in grado di evidenziare"
+msgstr "Avviso: il terminale non è in grado di evidenziare"
#: ../normal.c:2807
msgid "E348: No string under cursor"
@@ -4405,36 +4424,36 @@ msgstr "Batti :quit<Invio> per uscire da Vim"
#: ../ops.c:248
#, c-format
msgid "1 line %sed 1 time"
-msgstr "1 linea %sa 1 volta"
+msgstr "1 riga %sa 1 volta"
#: ../ops.c:250
#, c-format
msgid "1 line %sed %d times"
-msgstr "1 linea %sa %d volte"
+msgstr "1 riga %sa %d volte"
#: ../ops.c:253
#, c-format
msgid "%<PRId64> lines %sed 1 time"
-msgstr "%<PRId64> linee %se 1 volta"
+msgstr "%<PRId64> righe %se 1 volta"
#: ../ops.c:256
#, c-format
msgid "%<PRId64> lines %sed %d times"
-msgstr "%<PRId64> linee %se %d volte"
+msgstr "%<PRId64> righe %se %d volte"
#: ../ops.c:592
#, c-format
msgid "%<PRId64> lines to indent... "
-msgstr "%<PRId64> linee da rientrare... "
+msgstr "%<PRId64> righe da rientrare... "
#: ../ops.c:634
msgid "1 line indented "
-msgstr "1 linea rientrata "
+msgstr "1 riga rientrata "
#: ../ops.c:636
#, c-format
msgid "%<PRId64> lines indented "
-msgstr "%<PRId64> linee rientrate "
+msgstr "%<PRId64> righe rientrate "
#: ../ops.c:938
msgid "E748: No previously used register"
@@ -4447,30 +4466,30 @@ msgstr "non riesco a salvare in un registro; cancello comunque"
#: ../ops.c:1929
msgid "1 line changed"
-msgstr "1 linea cambiata"
+msgstr "1 riga cambiata"
#: ../ops.c:1931
#, c-format
msgid "%<PRId64> lines changed"
-msgstr "%<PRId64> linee cambiate"
+msgstr "%<PRId64> righe cambiate"
#: ../ops.c:2521
msgid "block of 1 line yanked"
-msgstr "blocco di 1 linea messo in registro"
+msgstr "blocco di 1 riga messo in registro"
#: ../ops.c:2523
msgid "1 line yanked"
-msgstr "1 linea messa in registro"
+msgstr "1 riga messa in registro"
#: ../ops.c:2525
#, c-format
msgid "block of %<PRId64> lines yanked"
-msgstr "blocco di %<PRId64> linee messo in registro"
+msgstr "blocco di %<PRId64> righe messo in registro"
#: ../ops.c:2528
#, c-format
msgid "%<PRId64> lines yanked"
-msgstr "%<PRId64> linee messe in registro"
+msgstr "%<PRId64> righe messe in registro"
#: ../ops.c:2710
#, c-format
@@ -4503,6 +4522,13 @@ msgstr ""
msgid "E574: Unknown register type %d"
msgstr "E574: Tipo di registro sconosciuto: %d"
+#: ../ops.c:4897
+msgid ""
+"E883: search pattern and expression register may not contain two or more "
+"lines"
+msgstr "E883: espressione di ricerca e registro dell'espressione non possono "
+"contenere due o più righe"
+
#: ../ops.c:5089
#, c-format
msgid "%<PRId64> Cols; "
@@ -4514,7 +4540,7 @@ msgid ""
"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
"%<PRId64> of %<PRId64> Bytes"
msgstr ""
-"Selezionate %s%<PRId64> di %<PRId64> Linee; %<PRId64> di %<PRId64> Parole; "
+"Selezionate %s%<PRId64> di %<PRId64> Righe; %<PRId64> di %<PRId64> Parole; "
"%<PRId64> di %<PRId64> Caratt."
#: ../ops.c:5105
@@ -4523,7 +4549,7 @@ msgid ""
"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
"%<PRId64> of %<PRId64> Chars; %<PRId64> of %<PRId64> Bytes"
msgstr ""
-"Selezionate %s%<PRId64> di %<PRId64> Linee; %<PRId64> di %<PRId64> Parole; "
+"Selezionate %s%<PRId64> di %<PRId64> Righe; %<PRId64> di %<PRId64> Parole; "
"%<PRId64> di %<PRId64> Caratt.; %<PRId64> di %<PRId64> Byte"
#: ../ops.c:5123
@@ -4532,7 +4558,7 @@ msgid ""
"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte "
"%<PRId64> of %<PRId64>"
msgstr ""
-"Col. %s di %s; Linea %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; "
+"Col. %s di %s; Riga %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; "
"Caratt. %<PRId64> di %<PRId64>"
#: ../ops.c:5133
@@ -4541,7 +4567,7 @@ msgid ""
"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char "
"%<PRId64> of %<PRId64>; Byte %<PRId64> of %<PRId64>"
msgstr ""
-"Col. %s di %s; Linea %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; "
+"Col. %s di %s; Riga %<PRId64> di %<PRId64>; Parola %<PRId64> di %<PRId64>; "
"Caratt. %<PRId64> di %<PRId64>; Byte %<PRId64> di %<PRId64>"
#: ../ops.c:5146
@@ -4587,6 +4613,11 @@ msgstr "E522: Non trovato in 'termcap'"
msgid "E539: Illegal character <%s>"
msgstr "E539: Carattere non ammesso <%s>"
+#: ../option.c:2253
+#, c-format
+msgid "For option %s"
+msgstr "Per opzione %s"
+
#: ../option.c:3862
msgid "E529: Cannot set 'term' to empty string"
msgstr "E529: Non posso assegnare a 'term' il valore 'stringa nulla'"
@@ -4665,7 +4696,7 @@ msgstr "W17: Arabo richiede UTF-8, esegui ':set encoding=utf-8'"
#: ../option.c:5623
#, c-format
msgid "E593: Need at least %d lines"
-msgstr "E593: Servono almeno %d linee"
+msgstr "E593: Servono almeno %d righe"
#: ../option.c:5631
#, c-format
@@ -4822,7 +4853,7 @@ msgstr "(%d di %d)%s%s: "
#: ../quickfix.c:1676
msgid " (line deleted)"
-msgstr " (linea cancellata)"
+msgstr " (riga cancellata)"
#: ../quickfix.c:1863
msgid "E380: At bottom of quickfix stack"
@@ -4973,6 +5004,10 @@ msgstr "E554: Errore sintattico in %s{...}"
msgid "External submatches:\n"
msgstr "Sotto-corrispondenze esterne:\n"
+#: ../regexp.c:2470
+msgid "E888: (NFA regexp) cannot repeat %s"
+msgstr "E888: (NFA regexp) non riesco a ripetere %s"
+
#: ../regexp.c:7022
msgid ""
"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
@@ -4981,6 +5016,10 @@ msgstr ""
"E864: \\%#= può essere seguito solo da 0, 1 o 2. Sarà usato il motore "
"automatico "
+#: ../regexp.c:7039
+msgid "Switching to backtracking RE engine for pattern: "
+msgstr "Passo alla ricerca di RE col vecchio metodo: "
+
#: ../regexp_nfa.c:239
msgid "E865: (NFA) Regexp end encountered prematurely"
msgstr "E865: (NFA) Fine prematura dell'espressione regolare"
@@ -5113,7 +5152,7 @@ msgstr " VISUALE"
#: ../screen.c:7470
msgid " VISUAL LINE"
-msgstr " VISUALE LINEA"
+msgstr " VISUALE RIGA"
#: ../screen.c:7471
msgid " VISUAL BLOCK"
@@ -5125,7 +5164,7 @@ msgstr " SELEZIONA"
#: ../screen.c:7473
msgid " SELECT LINE"
-msgstr " SELEZIONA LINEA"
+msgstr " SELEZIONA RIGA"
#: ../screen.c:7474
msgid " SELECT BLOCK"
@@ -5191,7 +5230,7 @@ msgstr "Cerco nel file incluso: %s"
#: ../search.c:4405
msgid "E387: Match is on current line"
-msgstr "E387: Corrispondenza nella linea corrente"
+msgstr "E387: Corrispondenza nella riga corrente"
#: ../search.c:4517
msgid "All included files were found"
@@ -5235,12 +5274,12 @@ msgstr "E758: File ortografico troncato"
#: ../spell.c:953
#, c-format
msgid "Trailing text in %s line %d: %s"
-msgstr "Testo in eccesso in %s linea %d: %s"
+msgstr "Testo in eccesso in %s riga %d: %s"
#: ../spell.c:954
#, c-format
msgid "Affix name too long in %s line %d: %s"
-msgstr "Nome affisso troppo lungo in %s linea %d: %s"
+msgstr "Nome affisso troppo lungo in %s riga %d: %s"
#: ../spell.c:955
msgid "E761: Format error in affix file FOL, LOW or UPP"
@@ -5261,7 +5300,7 @@ msgstr "E756: Controllo ortografico non abilitato"
#: ../spell.c:2249
#, c-format
msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
-msgstr "Attenzione: Non trovo lista parole \"%s.%s.spl\" o \"%s.ascii.spl\""
+msgstr "Avviso: Non trovo lista parole \"%s.%s.spl\" o \"%s.ascii.spl\""
#: ../spell.c:2473
#, c-format
@@ -5287,7 +5326,7 @@ msgstr "E770: Sezione non supportata nel file ortografico"
#: ../spell.c:3762
#, c-format
msgid "Warning: region %s not supported"
-msgstr "Attenzione: regione %s non supportata"
+msgstr "Avviso: regione %s non supportata"
#: ../spell.c:4550
#, c-format
@@ -5297,7 +5336,7 @@ msgstr "Lettura file affissi %s ..."
#: ../spell.c:4589 ../spell.c:5635 ../spell.c:6140
#, c-format
msgid "Conversion failure for word in %s line %d: %s"
-msgstr "Conversione fallita per una parola in %s linea %d: %s"
+msgstr "Conversione fallita per una parola in %s riga %d: %s"
#: ../spell.c:4630 ../spell.c:6170
#, c-format
@@ -5307,12 +5346,12 @@ msgstr "Conversione in %s non supportata: da %s a %s"
#: ../spell.c:4642
#, c-format
msgid "Invalid value for FLAG in %s line %d: %s"
-msgstr "Valore di FLAG non valido in %s linea %d: %s"
+msgstr "Valore di FLAG non valido in %s riga %d: %s"
#: ../spell.c:4655
#, c-format
msgid "FLAG after using flags in %s line %d: %s"
-msgstr "FLAG dopo l'uso di flags in %s linea %d: %s"
+msgstr "FLAG dopo l'uso di flags in %s riga %d: %s"
#: ../spell.c:4723
#, c-format
@@ -5321,7 +5360,7 @@ msgid ""
"%d"
msgstr ""
"Definire COMPOUNDFORBIDFLAG dopo l'elemento PFX potrebbe dare risultati "
-"errati in %s linea %d"
+"errati in %s riga %d"
#: ../spell.c:4731
#, c-format
@@ -5330,43 +5369,43 @@ msgid ""
"%d"
msgstr ""
"Definire COMPOUNDPERMITFLAG dopo l'elemento PFX potrebbe dare risultati "
-"errati in %s linea %d"
+"errati in %s riga %d"
#: ../spell.c:4747
#, c-format
msgid "Wrong COMPOUNDRULES value in %s line %d: %s"
-msgstr "Valore errato per COMPOUNDRULES in %s linea %d: %s"
+msgstr "Valore errato per COMPOUNDRULES in %s riga %d: %s"
#: ../spell.c:4771
#, c-format
msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s"
-msgstr "Valore errato per COMPOUNDWORDMAX in %s linea %d: %s"
+msgstr "Valore errato per COMPOUNDWORDMAX in %s riga %d: %s"
#: ../spell.c:4777
#, c-format
msgid "Wrong COMPOUNDMIN value in %s line %d: %s"
-msgstr "Valore errato per COMPOUNDMIN in %s linea %d: %s"
+msgstr "Valore errato per COMPOUNDMIN in %s riga %d: %s"
#: ../spell.c:4783
#, c-format
msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s"
-msgstr "Valore errato per COMPOUNDSYLMAX in %s linea %d: %s"
+msgstr "Valore errato per COMPOUNDSYLMAX in %s riga %d: %s"
#: ../spell.c:4795
#, c-format
msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s"
-msgstr "Valore errato per CHECKCOMPOUNDPATTERN in %s linea %d: %s"
+msgstr "Valore errato per CHECKCOMPOUNDPATTERN in %s riga %d: %s"
#: ../spell.c:4847
#, c-format
msgid "Different combining flag in continued affix block in %s line %d: %s"
msgstr ""
-"Flag combinazione diverso in blocco affissi continuo in %s linea %d: %s"
+"Flag combinazione diverso in blocco affissi continuo in %s riga %d: %s"
#: ../spell.c:4850
#, c-format
msgid "Duplicate affix in %s line %d: %s"
-msgstr "Affisso duplicato in %s linea %d: %s"
+msgstr "Affisso duplicato in %s riga %d: %s"
#: ../spell.c:4871
#, c-format
@@ -5375,42 +5414,42 @@ msgid ""
"line %d: %s"
msgstr ""
"Affisso usato anche per BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST "
-"in %s linea %d: %s"
+"in %s riga %d: %s"
#: ../spell.c:4893
#, c-format
msgid "Expected Y or N in %s line %d: %s"
-msgstr "Y o N deve essere presente in %s linea %d: %s"
+msgstr "Y o N deve essere presente in %s riga %d: %s"
#: ../spell.c:4968
#, c-format
msgid "Broken condition in %s line %d: %s"
-msgstr "Condizione non rispettata in %s linea %d: %s"
+msgstr "Condizione non rispettata in %s riga %d: %s"
#: ../spell.c:5091
#, c-format
msgid "Expected REP(SAL) count in %s line %d"
-msgstr "Contatore REP(SAL) necessario in %s linea %d"
+msgstr "Contatore REP(SAL) necessario in %s riga %d"
#: ../spell.c:5120
#, c-format
msgid "Expected MAP count in %s line %d"
-msgstr "Contatore MAP necessario in %s linea %d"
+msgstr "Contatore MAP necessario in %s riga %d"
#: ../spell.c:5132
#, c-format
msgid "Duplicate character in MAP in %s line %d"
-msgstr "Carattere duplicato in MAP in %s linea %d"
+msgstr "Carattere duplicato in MAP in %s riga %d"
#: ../spell.c:5176
#, c-format
msgid "Unrecognized or duplicate item in %s line %d: %s"
-msgstr "Elemento non riconosciuto o duplicato in %s linea %d: %s"
+msgstr "Elemento non riconosciuto o duplicato in %s riga %d: %s"
#: ../spell.c:5197
#, c-format
msgid "Missing FOL/LOW/UPP line in %s"
-msgstr "Linea FOL/LOW/UPP mancante in %s"
+msgstr "Riga FOL/LOW/UPP mancante in %s"
#: ../spell.c:5220
msgid "COMPOUNDSYLMAX used without SYLLABLE"
@@ -5431,22 +5470,22 @@ msgstr "Troppi suffissi e/o flag composti"
#: ../spell.c:5250
#, c-format
msgid "Missing SOFO%s line in %s"
-msgstr "Linea SOFO%s mancante in %s"
+msgstr "Riga SOFO%s mancante in %s"
#: ../spell.c:5253
#, c-format
msgid "Both SAL and SOFO lines in %s"
-msgstr "Linee sia SAL che SOFO in %s"
+msgstr "Riga sia SAL che SOFO in %s"
#: ../spell.c:5331
#, c-format
msgid "Flag is not a number in %s line %d: %s"
-msgstr "Il flag non è un numero in %s linea %d: %s"
+msgstr "Il flag non è un numero in %s riga %d: %s"
#: ../spell.c:5334
#, c-format
msgid "Illegal flag in %s line %d: %s"
-msgstr "Flag non ammesso in %s linea %d: %s"
+msgstr "Flag non ammesso in %s riga %d: %s"
#: ../spell.c:5493 ../spell.c:5501
#, c-format
@@ -5466,17 +5505,17 @@ msgstr "E760: Nessun contatore parole in %s"
#: ../spell.c:5669
#, c-format
msgid "line %6d, word %6d - %s"
-msgstr "linea %6d, parola %6d - %s"
+msgstr "riga %6d, parola %6d - %s"
#: ../spell.c:5691
#, c-format
msgid "Duplicate word in %s line %d: %s"
-msgstr "Parola duplicata in %s linea %d: %s"
+msgstr "Parola duplicata in %s riga %d: %s"
#: ../spell.c:5694
#, c-format
msgid "First duplicate word in %s line %d: %s"
-msgstr "Prima parola duplicata in %s linea %d: %s"
+msgstr "Prima parola duplicata in %s riga %d: %s"
#: ../spell.c:5746
#, c-format
@@ -5496,37 +5535,37 @@ msgstr "Lettura file parole %s ..."
#: ../spell.c:6155
#, c-format
msgid "Duplicate /encoding= line ignored in %s line %d: %s"
-msgstr "Linea /encoding= duplicata ignorata in %s linea %d: %s"
+msgstr "Riga /encoding= duplicata ignorata in %s riga %d: %s"
#: ../spell.c:6159
#, c-format
msgid "/encoding= line after word ignored in %s line %d: %s"
-msgstr "Linea /encoding= dopo parola ignorata in %s linea %d: %s"
+msgstr "Riga /encoding= dopo parola ignorata in %s riga %d: %s"
#: ../spell.c:6180
#, c-format
msgid "Duplicate /regions= line ignored in %s line %d: %s"
-msgstr "Linea /regions= duplicata ignorata in %s linea %d: %s"
+msgstr "Riga /regions= duplicata ignorata in %s riga %d: %s"
#: ../spell.c:6185
#, c-format
msgid "Too many regions in %s line %d: %s"
-msgstr "Troppe regioni in %s linea %d: %s"
+msgstr "Troppe regioni in %s riga %d: %s"
#: ../spell.c:6198
#, c-format
msgid "/ line ignored in %s line %d: %s"
-msgstr "Linea / ignorata in %s linea %d: %s"
+msgstr "Riga / ignorata in %s riga %d: %s"
#: ../spell.c:6224
#, c-format
msgid "Invalid region nr in %s line %d: %s"
-msgstr "N. regione non valido in %s linea %d: %s"
+msgstr "N. regione non valido in %s riga %d: %s"
#: ../spell.c:6230
#, c-format
msgid "Unrecognized flags in %s line %d: %s"
-msgstr "Flag non riconosciuti in %s linea %d: %s"
+msgstr "Flag non riconosciuti in %s riga %d: %s"
#: ../spell.c:6257
#, c-format
@@ -5583,7 +5622,7 @@ msgstr "E755: Regione non valida in %s"
#: ../spell.c:7907
msgid "Warning: both compounding and NOBREAK specified"
-msgstr "Attenzione: specificati sia composizione sia NOBREAK"
+msgstr "Avviso: specificati sia composizione sia NOBREAK"
#: ../spell.c:7920
#, c-format
@@ -5700,7 +5739,7 @@ msgstr "la sincronizzazione inizia "
#: ../syntax.c:3443 ../syntax.c:3506
msgid " lines before top line"
-msgstr " linee prima della linea iniziale"
+msgstr " righe prima della riga iniziale"
#: ../syntax.c:3448
msgid ""
@@ -5745,7 +5784,7 @@ msgstr "; corrisp. "
#: ../syntax.c:3515
msgid " line breaks"
-msgstr " interruzioni di linea"
+msgstr " interruzioni di riga"
#: ../syntax.c:4076
msgid "E395: contains argument not accepted here"
@@ -5809,7 +5848,7 @@ msgstr "E402: Spazzatura dopo espressione: %s"
#: ../syntax.c:5120
msgid "E403: syntax sync: line continuations pattern specified twice"
msgstr ""
-"E403: syntax sync: espressione di continuazione linea specificata due volte"
+"E403: syntax sync: espressione di continuazione riga specificata due volte"
#: ../syntax.c:5169
#, c-format
@@ -5998,7 +6037,7 @@ msgid ""
" # TO tag FROM line in file/text"
msgstr ""
"\n"
-" # A tag DA__ linea in file/testo"
+" # A tag DA__ riga in file/testo"
#: ../tag.c:1303
#, c-format
@@ -6007,7 +6046,7 @@ msgstr "Ricerca nel tag file %s"
#: ../tag.c:1545
msgid "Ignoring long line in tags file"
-msgstr "Linea lunga ignorata nel tag file"
+msgstr "Riga lunga ignorata nel tag file"
#: ../tag.c:1915
#, c-format
@@ -6177,23 +6216,23 @@ msgstr "E830: Undo numero %<PRId64> non trovato"
#: ../undo.c:1979
msgid "E438: u_undo: line numbers wrong"
-msgstr "E438: u_undo: numeri linee errati"
+msgstr "E438: u_undo: numeri righe errati"
#: ../undo.c:2183
msgid "more line"
-msgstr "linea in più"
+msgstr "riga in più"
#: ../undo.c:2185
msgid "more lines"
-msgstr "linee in più"
+msgstr "righe in più"
#: ../undo.c:2187
msgid "line less"
-msgstr "linea in meno"
+msgstr "riga in meno"
#: ../undo.c:2189
msgid "fewer lines"
-msgstr "linee in meno"
+msgstr "righe in meno"
#: ../undo.c:2193
msgid "change"
@@ -6239,7 +6278,7 @@ msgstr "E439: lista 'undo' non valida"
#: ../undo.c:2495
msgid "E440: undo line missing"
-msgstr "E440: linea di 'undo' mancante"
+msgstr "E440: riga di 'undo' mancante"
#: ../version.c:600
msgid ""
@@ -6439,6 +6478,10 @@ msgstr "E445: Altre finestre contengono modifiche"
msgid "E446: No file name under cursor"
msgstr "E446: Nessun nome file sotto il cursore"
+#: ../window.c:5484
+msgid "List or number required"
+msgstr "È necessaria una lista o un numero"
+
#~ msgid "E831: bf_key_init() called with empty password"
#~ msgstr "E831: chiamata a bf_key_init() con password nulla"
@@ -6524,15 +6567,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "Reading from stdin..."
#~ msgstr "Leggo da 'stdin'..."
-#~ msgid "[blowfish]"
-#~ msgstr "[blowfish]"
-
-#~ msgid "[crypted]"
-#~ msgstr "[cifrato]"
-
-#~ msgid "E821: File is encrypted with unknown method"
-#~ msgstr "E821: File cifrato con metodo sconosciuto"
-
#~ msgid "NetBeans disallows writes of unmodified buffers"
#~ msgstr "NetBeans non permette la scrittura di un buffer non modificato"
@@ -6648,8 +6682,8 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "Vim: Received \"die\" request from session manager\n"
#~ msgstr "Vim: Ricevuta richiesta \"die\" dal session manager\n"
-#~ msgid "Close"
-#~ msgstr "Chiusura"
+#~ msgid "Close tab"
+#~ msgstr "Chiudi linguetta"
#~ msgid "New tab"
#~ msgstr "Nuova linguetta"
@@ -6705,9 +6739,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "E672: Unable to open window inside MDI application"
#~ msgstr "E672: Non posso aprire la finestra in un'applicazione MDI"
-#~ msgid "Close tab"
-#~ msgstr "Chiudi linguetta"
-
#~ msgid "Open tab..."
#~ msgstr "Apri linguetta..."
@@ -6830,13 +6861,13 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "non sono riuscito ad aprire il buffer"
#~ msgid "cannot delete line"
-#~ msgstr "non posso cancellare la linea"
+#~ msgstr "non posso cancellare la riga"
#~ msgid "cannot replace line"
-#~ msgstr "non posso sostituire la linea"
+#~ msgstr "non posso sostituire la riga"
#~ msgid "cannot insert line"
-#~ msgstr "non posso inserire la linea"
+#~ msgstr "non posso inserire la riga"
#~ msgid "string cannot contain newlines"
#~ msgstr "la stringa non può contenere caratteri 'A CAPO'"
@@ -6857,7 +6888,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "finestra non valida"
#~ msgid "linenr out of range"
-#~ msgstr "numero linea non nell'intervallo"
+#~ msgstr "numero riga non nell'intervallo"
#~ msgid "not allowed in the Vim sandbox"
#~ msgstr "non ammesso in ambiente protetto"
@@ -6865,9 +6896,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "E836: This Vim cannot execute :python after using :py3"
#~ msgstr "E836: Python: Impossibile usare :py e :py3 nella stessa sessione"
-#~ msgid "E837: This Vim cannot execute :py3 after using :python"
-#~ msgstr "E837: Impossibile usare ora :py3 dopo aver usato :python"
-
#~ msgid ""
#~ "E263: Sorry, this command is disabled, the Python library could not be "
#~ "loaded."
@@ -6875,14 +6903,18 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ "E263: Spiacente, comando non disponibile, non riesco a caricare libreria "
#~ "programmi Python."
+#~ msgid ""
+#~ "E887: Sorry, this command is disabled, the Python's site module could not be "
+#~ "loaded."
+#~ msgstr ""
+#~ "E887: Spiacente, comando non disponibile, non riesco a caricare il modulo "
+#~ "Python locale."
+
#~ msgid "E659: Cannot invoke Python recursively"
#~ msgstr "E659: Python non può essere chiamato ricorsivamente"
-#~ msgid "line number out of range"
-#~ msgstr "numero linea non nell'intervallo"
-
-#~ msgid "invalid mark name"
-#~ msgstr "nome di mark non valido"
+#~ msgid "E837: This Vim cannot execute :py3 after using :python"
+#~ msgstr "E837: Impossibile usare ora :py3 dopo aver usato :python"
#~ msgid "E265: $_ must be an instance of String"
#~ msgstr "E265: $_ deve essere un'istanza di String"
@@ -7010,7 +7042,10 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "non ancora implementato"
#~ msgid "cannot set line(s)"
-#~ msgstr "non posso impostare linea(e)"
+#~ msgstr "non posso impostare riga(he)"
+
+#~ msgid "invalid mark name"
+#~ msgstr "nome di mark non valido"
#~ msgid "mark not set"
#~ msgstr "mark non impostato"
@@ -7019,7 +7054,10 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "riga %d colonna %d"
#~ msgid "cannot insert/append line"
-#~ msgstr "non riesco a inserire/aggiungere linea"
+#~ msgstr "non riesco a inserire/aggiungere riga"
+
+#~ msgid "line number out of range"
+#~ msgstr "numero linea non nell'intervallo"
#~ msgid "unknown flag: "
#~ msgstr "opzione inesistente: "
@@ -7067,7 +7105,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "E572: codice di uscita %d"
#~ msgid "cannot get line"
-#~ msgstr "non riesco a ottenere la linea"
+#~ msgstr "non riesco a ottenere la riga"
#~ msgid "Unable to register a command server name"
#~ msgstr "Non riesco a registrare un nome di server comando"
@@ -7293,7 +7331,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "E286: Apertura 'input method' fallita"
#~ msgid "E287: Warning: Could not set destroy callback to IM"
-#~ msgstr "E287: Attenzione: Non posso assegnare IM a 'destroy callback'"
+#~ msgstr "E287: Avviso: Non posso assegnare IM a 'destroy callback'"
#~ msgid "E288: input method doesn't support any style"
#~ msgstr "E288: 'input method' non sopporta alcuno stile"
@@ -7365,12 +7403,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "E338: Sorry, no file browser in console mode"
#~ msgstr "E338: Spiacente, niente esplorazione file in modalità console"
-#~ msgid "Vim: preserving files...\n"
-#~ msgstr "Vim: preservo file...\n"
-
-#~ msgid "Vim: Finished.\n"
-#~ msgstr "Vim: Finito.\n"
-
#~ msgid "ERROR: "
#~ msgstr "ERRORE: "
@@ -7391,7 +7423,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ "\n"
#~ msgid "E340: Line is becoming too long"
-#~ msgstr "E340: La linea sta diventando troppo lunga"
+#~ msgstr "E340: La riga sta diventando troppo lunga"
#~ msgid "E341: Internal error: lalloc(%<PRId64>, )"
#~ msgstr "E341: Errore interno: lalloc(%<PRId64>, )"
@@ -7399,15 +7431,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "E547: Illegal mouseshape"
#~ msgstr "E547: Forma del mouse non valida"
-#~ msgid "Enter encryption key: "
-#~ msgstr "Immetti chiave di cifratura: "
-
-#~ msgid "Enter same key again: "
-#~ msgstr "Ribatti per conferma la stessa chiave: "
-
-#~ msgid "Keys don't match!"
-#~ msgstr "Le chiavi non corrispondono!"
-
#~ msgid "Cannot connect to Netbeans #2"
#~ msgstr "Non posso connettermi a Netbeans #2"
@@ -7437,7 +7460,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "E775: Funzionalità [eval] non disponibile"
#~ msgid "freeing %<PRId64> lines"
-#~ msgstr "libero %<PRId64> linee"
+#~ msgstr "libero %<PRId64> righe"
#~ msgid "E530: Cannot change term in GUI"
#~ msgstr "E530: Non posso modificare 'term' mentre sono nella GUI"
@@ -7538,15 +7561,6 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "E245: Illegal char '%c' in font name \"%s\""
#~ msgstr "E245: Carattere non ammesso '%c' nel font di nome \"%s\""
-#~ msgid "Vim: Double signal, exiting\n"
-#~ msgstr "Vim: Segnale doppio, esco\n"
-
-#~ msgid "Vim: Caught deadly signal %s\n"
-#~ msgstr "Vim: Intercettato segnale fatale %s\n"
-
-#~ msgid "Vim: Caught deadly signal\n"
-#~ msgstr "Vim: Intercettato segnale fatale\n"
-
#~ msgid "Opening the X display took %<PRId64> msec"
#~ msgstr "Attivazione visualizzazione X ha richiesto %<PRId64> msec"
@@ -7610,7 +7624,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr "XSMP SmcOpenConnection fallita: %s"
#~ msgid "At line"
-#~ msgstr "Alla linea"
+#~ msgstr "Alla riga"
#~ msgid "Could not load vim32.dll!"
#~ msgstr "Non riesco a caricare vim32.dll!"
@@ -7875,7 +7889,7 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgstr " modo Vim predefinito "
#~ msgid "WARNING: Windows 95/98/ME detected"
-#~ msgstr "ATTENZIONE: Trovato Windows 95/98/ME"
+#~ msgstr "AVVISO: Trovato Windows 95/98/ME"
#~ msgid "type :help windows95<Enter> for info on this"
#~ msgstr "batti :help windows95<Enter> per info al riguardo"
@@ -7962,6 +7976,22 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "Need encryption key for \"%s\""
#~ msgstr "Serve una chiave di cifratura per \"%s\""
+#~ msgid "empty keys are not allowed"
+#~ msgstr "chiavi nulle non consentite"
+
+#~ msgid "dictionary is locked"
+#~ msgstr "il dizionario è bloccato"
+
+#~ msgid "list is locked"
+#~ msgstr "la lista è bloccata"
+
+#~ msgid "failed to add key '%s' to dictionary"
+#~ msgstr "non non riusciato ad aggiungere la chiave '%s' al dizionario"
+
+#~ #, c-format
+#~ msgid "index must be int or slice, not %s"
+#~ msgstr "l'indice deve'essere un intero o un intervallo, non %s"
+
#~ msgid "expected str() or unicode() instance, but got %s"
#~ msgstr "attesa istanza di str() o unicode(), trovato invece %s"
@@ -7985,8 +8015,8 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "value is too small to fit into C int type"
#~ msgstr "valore troppo piccolo per il tipo int del C"
-#~ msgid "number must be greater then zero"
-#~ msgstr "il numero dev'essere maggiore di zero"
+#~ msgid "number must be greater than zero"
+#~ msgstr "il numero deve essere maggiore di zero"
#~ msgid "number must be greater or equal to zero"
#~ msgstr "il numero dev'essere maggiore o uguale a zero"
@@ -8005,7 +8035,8 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "expected 3-tuple as imp.find_module() result, but got %s"
#~ msgstr ""
-#~ "atteso terzetto come risultato di imp.find_module(), trovato invece %s"
+#~ "atteso terzetto come risultato di imp.find_module(), trovato invece tuple di "
+#~ "dimens. %d"
#~ msgid ""
#~ "expected 3-tuple as imp.find_module() result, but got tuple of size %d"
@@ -8042,15 +8073,30 @@ msgstr "E446: Nessun nome file sotto il cursore"
#~ msgid "internal error: failed to get vim list item %d"
#~ msgstr "errore interno: non ho potuto ottenere l'elemento di vim list %d"
-#~ msgid "failed to add item to list"
-#~ msgstr "non ho potuto aggiungere un elemento alla lista"
+#~ msgid "slice step cannot be zero"
+#~ msgstr "il passo scorrendo un intervallo non può essere zero"
+
+#~ #, c-format
+#~ msgid "attempt to assign sequence of size greater than %d to extended slice"
+#~ msgstr "tentativo di assegnare una sequenza maggiore di %d a un intervallo "
+#~ "esteso"
#~ msgid "internal error: no vim list item %d"
#~ msgstr "errore interno: non c'è un elemento di vim list %d"
+#~ msgid "internal error: not enough list items"
+#~ msgstr "errore interno: non ci sono abbastanza elementi per la lista"
+
#~ msgid "internal error: failed to add item to list"
#~ msgstr "errore interno: non ho potuto aggiungere un elemento alla lista"
+#~ msgid "attempt to assign sequence of size %d to extended slice of size %d"
+#~ msgstr "tentativo di assegnare sequenza di dimensione %d a un intervallo "
+#~ " esteso di dimensione %d"
+
+#~ msgid "failed to add item to list"
+#~ msgstr "non ho potuto aggiungere un elemento alla lista"
+
#~ msgid "cannot delete vim.List attributes"
#~ msgstr "non riesco a cancellare gli attributi vim.List"
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 001740943f..5ad621e666 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -545,7 +545,11 @@ static int pum_set_selected(int n, int repeat)
g_do_tagpreview = (int)p_pvh;
}
RedrawingDisabled++;
+ // Prevent undo sync here, if an autocommand syncs undo weird
+ // things can happen to the undo tree.
+ no_u_sync++;
resized = prepare_tagpreview(false);
+ no_u_sync--;
RedrawingDisabled--;
g_do_tagpreview = 0;
@@ -629,7 +633,9 @@ static int pum_set_selected(int n, int repeat)
// the window when needed, otherwise it will always be
// redraw.
if (resized) {
+ no_u_sync++;
win_enter(curwin_save, true);
+ no_u_sync--;
update_topline();
}
@@ -640,7 +646,9 @@ static int pum_set_selected(int n, int repeat)
pum_do_redraw = FALSE;
if (!resized && win_valid(curwin_save)) {
+ no_u_sync++;
win_enter(curwin_save, true);
+ no_u_sync--;
}
// May need to update the screen again when there are
diff --git a/src/nvim/pos.h b/src/nvim/pos.h
index 7071df51e8..864f3fe866 100644
--- a/src/nvim/pos.h
+++ b/src/nvim/pos.h
@@ -2,7 +2,11 @@
#define NVIM_POS_H
typedef long linenr_T; // line number type
-typedef int colnr_T; // column number type
+
+/// Column number type
+typedef int colnr_T;
+/// Format used to print values which have colnr_T type
+#define PRIdCOLNR "d"
#define MAXLNUM 0x7fffffff // maximum (invalid) line number
#define MAXCOL 0x7fffffff // maximum column number, 31 bits
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index f3abf864fb..151b9d3790 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -39,7 +39,6 @@
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
-#include "nvim/tempfile.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@@ -161,9 +160,6 @@ qf_init (
{
qf_info_T *qi = &ql_info;
- if (efile == NULL)
- return FAIL;
-
if (wp != NULL) {
qi = ll_get_or_alloc_list(wp);
}
@@ -200,9 +196,8 @@ qf_init_ext (
char_u *pattern;
char_u *fmtstr = NULL;
int col = 0;
- char_u use_viscol = FALSE;
- int type = 0;
- int valid;
+ bool use_viscol = false;
+ char_u type = 0;
linenr_T buflnum = lnumfirst;
long lnum = 0L;
int enr = 0;
@@ -220,16 +215,16 @@ qf_init_ext (
int i;
int round;
int idx = 0;
- int multiline = FALSE;
- int multiignore = FALSE;
- int multiscan = FALSE;
- int retval = -1; /* default: return error flag */
- char_u *directory = NULL;
- char_u *currfile = NULL;
- char_u *tail = NULL;
- char_u *p_str = NULL;
- listitem_T *p_li = NULL;
- struct dir_stack_T *file_stack = NULL;
+ bool multiline = false;
+ bool multiignore = false;
+ bool multiscan = false;
+ int retval = -1; // default: return error flag
+ char_u *directory = NULL;
+ char_u *currfile = NULL;
+ char_u *tail = NULL;
+ char_u *p_str = NULL;
+ listitem_T *p_li = NULL;
+ struct dir_stack_T *file_stack = NULL;
regmatch_T regmatch;
static struct fmtpattern {
char_u convchar;
@@ -278,15 +273,16 @@ qf_init_ext (
/*
* Get some space to modify the format string into.
*/
- i = 3 * FMT_PATTERNS + 4 * (int)STRLEN(efm);
- for (round = FMT_PATTERNS; round > 0; )
- i += (int)STRLEN(fmt_pat[--round].pattern);
+ size_t fmtstr_size = 3 * FMT_PATTERNS + 4 * STRLEN(efm);
+ for (round = FMT_PATTERNS; round > 0; ) {
+ fmtstr_size += STRLEN(fmt_pat[--round].pattern);
+ }
#ifdef COLON_IN_FILENAME
- i += 12; /* "%f" can become twelve chars longer */
+ fmtstr_size += 12; // "%f" can become twelve chars longer
#else
- i += 2; /* "%f" can become two chars longer */
+ fmtstr_size += 2; // "%f" can become two chars longer
#endif
- fmtstr = xmalloc(i);
+ fmtstr = xmalloc(fmtstr_size);
while (efm[0] != NUL) {
/*
@@ -530,11 +526,9 @@ qf_init_ext (
fmt_start = NULL;
}
- /*
- * Try to match each part of 'errorformat' until we find a complete
- * match or no match.
- */
- valid = TRUE;
+ // Try to match each part of 'errorformat' until we find a complete
+ // match or no match.
+ bool valid = true;
restofline:
for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) {
idx = fmt_ptr->prefix;
@@ -546,7 +540,7 @@ restofline:
errmsg[0] = NUL;
lnum = 0;
col = 0;
- use_viscol = FALSE;
+ use_viscol = false;
enr = -1;
type = 0;
tail = NULL;
@@ -555,25 +549,23 @@ restofline:
int r = vim_regexec(&regmatch, IObuff, (colnr_T)0);
fmt_ptr->prog = regmatch.regprog;
if (r) {
- if ((idx == 'C' || idx == 'Z') && !multiline)
+ if ((idx == 'C' || idx == 'Z') && !multiline) {
continue;
- if (vim_strchr((char_u *)"EWI", idx) != NULL)
- type = idx;
- else
+ }
+ if (vim_strchr((char_u *)"EWI", idx) != NULL) {
+ type = (char_u)idx;
+ } else {
type = 0;
- /*
- * Extract error message data from matched line.
- * We check for an actual submatch, because "\[" and "\]" in
- * the 'errorformat' may cause the wrong submatch to be used.
- */
- if ((i = (int)fmt_ptr->addr[0]) > 0) { /* %f */
- int c;
-
- if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL)
+ }
+ // Extract error message data from matched line.
+ // We check for an actual submatch, because "\[" and "\]" in
+ // the 'errorformat' may cause the wrong submatch to be used.
+ if ((i = (int)fmt_ptr->addr[0]) > 0) { // %f
+ if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) {
continue;
-
- /* Expand ~/file and $HOME/file to full path. */
- c = *regmatch.endp[i];
+ }
+ // Expand ~/file and $HOME/file to full path.
+ char_u c = *regmatch.endp[i];
*regmatch.endp[i] = NUL;
expand_env(regmatch.startp[i], namebuf, CMDBUFFSIZE);
*regmatch.endp[i] = c;
@@ -629,14 +621,14 @@ restofline:
col -= col % 8;
}
}
- ++col;
- use_viscol = TRUE;
+ col++;
+ use_viscol = true;
}
if ((i = (int)fmt_ptr->addr[8]) > 0) { /* %v */
if (regmatch.startp[i] == NULL)
continue;
col = (int)atol((char *)regmatch.startp[i]);
- use_viscol = TRUE;
+ use_viscol = true;
}
if ((i = (int)fmt_ptr->addr[9]) > 0) { /* %s */
if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL)
@@ -653,7 +645,7 @@ restofline:
break;
}
}
- multiscan = FALSE;
+ multiscan = false;
if (fmt_ptr == NULL || idx == 'D' || idx == 'X') {
if (fmt_ptr != NULL) {
@@ -667,20 +659,21 @@ restofline:
} else if (idx == 'X') /* leave directory */
directory = qf_pop_dir(&dir_stack);
}
- namebuf[0] = NUL; /* no match found, remove file name */
- lnum = 0; /* don't jump to this line */
- valid = FALSE;
- STRCPY(errmsg, IObuff); /* copy whole line to error message */
- if (fmt_ptr == NULL)
- multiline = multiignore = FALSE;
+ namebuf[0] = NUL; // no match found, remove file name
+ lnum = 0; // don't jump to this line
+ valid = false;
+ STRCPY(errmsg, IObuff); // copy whole line to error message
+ if (fmt_ptr == NULL) {
+ multiline = multiignore = false;
+ }
} else if (fmt_ptr != NULL) {
/* honor %> item */
if (fmt_ptr->conthere)
fmt_start = fmt_ptr;
if (vim_strchr((char_u *)"AEWI", idx) != NULL) {
- multiline = TRUE; /* start of a multi-line message */
- multiignore = FALSE; /* reset continuation */
+ multiline = true; // start of a multi-line message
+ multiignore = false; // reset continuation
} else if (vim_strchr((char_u *)"CZ", idx)
!= NULL) { /* continuation of multi-line msg */
if (qfprev == NULL)
@@ -702,15 +695,17 @@ restofline:
qfprev->qf_viscol = use_viscol;
if (!qfprev->qf_fnum)
qfprev->qf_fnum = qf_get_fnum(directory,
- *namebuf || directory ? namebuf
- : currfile && valid ? currfile : 0);
- if (idx == 'Z')
- multiline = multiignore = FALSE;
+ *namebuf
+ || directory ? namebuf : currfile
+ && valid ? currfile : 0);
+ if (idx == 'Z') {
+ multiline = multiignore = false;
+ }
line_breakcheck();
continue;
} else if (vim_strchr((char_u *)"OPQ", idx) != NULL) {
- /* global file names */
- valid = FALSE;
+ // global file names
+ valid = false;
if (*namebuf == NUL || os_file_exists(namebuf)) {
if (*namebuf && idx == 'P')
currfile = qf_push_dir(namebuf, &file_stack);
@@ -719,14 +714,15 @@ restofline:
*namebuf = NUL;
if (tail && *tail) {
STRMOVE(IObuff, skipwhite(tail));
- multiscan = TRUE;
+ multiscan = true;
goto restofline;
}
}
}
- if (fmt_ptr->flags == '-') { /* generally exclude this line */
- if (multiline)
- multiignore = TRUE; /* also exclude continuation lines */
+ if (fmt_ptr->flags == '-') { // generally exclude this line
+ if (multiline) {
+ multiignore = true; // also exclude continuation lines
+ }
continue;
}
}
@@ -867,26 +863,27 @@ void qf_free_all(win_T *wp)
qf_free(qi, i);
}
-/*
- * Add an entry to the end of the list of errors.
- * Returns OK or FAIL.
- */
-static int
-qf_add_entry (
- qf_info_T *qi, /* quickfix list */
- qfline_T **prevp, /* nonnull pointer (to previously added entry or NULL) */
- char_u *dir, /* optional directory name */
- char_u *fname, /* file name or NULL */
- int bufnum, /* buffer number or zero */
- char_u *mesg, /* message */
- long lnum, /* line number */
- int col, /* column */
- int vis_col, /* using visual column */
- char_u *pattern, /* search pattern */
- int nr, /* error number */
- int type, /* type character */
- int valid /* valid entry */
-)
+/// Add an entry to the end of the list of errors.
+///
+/// @param qi quickfix list
+/// @param prevp nonnull pointer (to previously added entry or NULL)
+/// @param dir optional directory name
+/// @param fname file name or NULL
+/// @param bufnum buffer number or zero
+/// @param mesg message
+/// @param lnum line number
+/// @param col column
+/// @param vis_col using visual column
+/// @param pattern search pattern
+/// @param nr error number
+/// @param type type character
+/// @param valid valid entry
+///
+/// @returns OK or FAIL.
+static int qf_add_entry(qf_info_T *qi, qfline_T **prevp, char_u *dir,
+ char_u *fname, int bufnum, char_u *mesg, long lnum,
+ int col, char_u vis_col, char_u *pattern, int nr,
+ char_u type, char_u valid)
{
qfline_T *qfp = xmalloc(sizeof(qfline_T));
@@ -1657,12 +1654,13 @@ win_found:
* flag is present in 'shortmess'; But when not jumping, print the
* whole message. */
i = msg_scroll;
- if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum)
- msg_scroll = TRUE;
- else if (!msg_scrolled && shortmess(SHM_OVERALL))
- msg_scroll = FALSE;
- msg_attr_keep(IObuff, 0, TRUE);
- msg_scroll = i;
+ if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) {
+ msg_scroll = true;
+ } else if (!msg_scrolled && shortmess(SHM_OVERALL)) {
+ msg_scroll = false;
+ }
+ msg_attr_keep(IObuff, 0, true);
+ msg_scroll = (int)i;
}
} else {
if (opened_window)
@@ -1827,10 +1825,12 @@ void qf_age(exarg_T *eap)
}
}
- if (eap->addr_count != 0)
- count = eap->line2;
- else
+ if (eap->addr_count != 0) {
+ assert(eap->line2 <= INT_MAX);
+ count = (int)eap->line2;
+ } else {
count = 1;
+ }
while (count--) {
if (eap->cmdidx == CMD_colder || eap->cmdidx == CMD_lolder) {
if (qi->qf_curlist == 0) {
@@ -1882,6 +1882,8 @@ static void qf_free(qf_info_T *qi, int idx)
--qi->qf_lists[idx].qf_count;
}
xfree(qi->qf_lists[idx].qf_title);
+ qi->qf_lists[idx].qf_start = NULL;
+ qi->qf_lists[idx].qf_ptr = NULL;
qi->qf_lists[idx].qf_title = NULL;
qi->qf_lists[idx].qf_index = 0;
}
@@ -1948,7 +1950,7 @@ static char_u *qf_types(int c, int nr)
p = (char_u *)"";
else {
cc[0] = ' ';
- cc[1] = c;
+ cc[1] = (char_u)c;
cc[2] = NUL;
p = cc;
}
@@ -2036,12 +2038,13 @@ void ex_copen(exarg_T *eap)
}
}
- if (eap->addr_count != 0)
- height = eap->line2;
- else
+ if (eap->addr_count != 0) {
+ assert(eap->line2 <= INT_MAX);
+ height = (int)eap->line2;
+ } else {
height = QF_WINHEIGHT;
-
- reset_VIsual_and_resel(); /* stop Visual mode */
+ }
+ reset_VIsual_and_resel(); // stop Visual mode
/*
* Find existing quickfix window, or open a new one.
@@ -2299,13 +2302,15 @@ static void qf_fill_buffer(qf_info_T *qi)
if (qfp->qf_fnum != 0
&& (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
&& errbuf->b_fname != NULL) {
- if (qfp->qf_type == 1) /* :helpgrep */
- STRCPY(IObuff, path_tail(errbuf->b_fname));
- else
- STRCPY(IObuff, errbuf->b_fname);
+ if (qfp->qf_type == 1) { // :helpgrep
+ STRLCPY(IObuff, path_tail(errbuf->b_fname), sizeof(IObuff));
+ } else {
+ STRLCPY(IObuff, errbuf->b_fname, sizeof(IObuff));
+ }
len = (int)STRLEN(IObuff);
- } else
+ } else {
len = 0;
+ }
IObuff[len++] = '|';
if (qfp->qf_lnum > 0) {
@@ -2431,8 +2436,6 @@ int grep_internal(cmdidx_T cmdidx)
void ex_make(exarg_T *eap)
{
char_u *fname;
- char_u *cmd;
- unsigned len;
win_T *wp = NULL;
qf_info_T *qi = &ql_info;
int res;
@@ -2470,29 +2473,28 @@ void ex_make(exarg_T *eap)
return;
os_remove((char *)fname); // in case it's not unique
- /*
- * If 'shellpipe' empty: don't redirect to 'errorfile'.
- */
- len = (unsigned)STRLEN(p_shq) * 2 + (unsigned)STRLEN(eap->arg) + 1;
- if (*p_sp != NUL)
- len += (unsigned)STRLEN(p_sp) + (unsigned)STRLEN(fname) + 3;
- cmd = xmalloc(len);
- sprintf((char *)cmd, "%s%s%s", (char *)p_shq, (char *)eap->arg,
- (char *)p_shq);
- if (*p_sp != NUL)
- append_redir(cmd, len, p_sp, fname);
- /*
- * Output a newline if there's something else than the :make command that
- * was typed (in which case the cursor is in column 0).
- */
- if (msg_col == 0)
- msg_didout = FALSE;
+ // If 'shellpipe' empty: don't redirect to 'errorfile'.
+ const size_t len = (STRLEN(p_shq) * 2 + STRLEN(eap->arg) + 1
+ + (*p_sp == NUL
+ ? 0
+ : STRLEN(p_sp) + STRLEN(fname) + 3));
+ char *const cmd = xmalloc(len);
+ snprintf(cmd, len, "%s%s%s", (char *)p_shq, (char *)eap->arg,
+ (char *)p_shq);
+ if (*p_sp != NUL) {
+ append_redir(cmd, len, (char *) p_sp, (char *) fname);
+ }
+ // Output a newline if there's something else than the :make command that
+ // was typed (in which case the cursor is in column 0).
+ if (msg_col == 0) {
+ msg_didout = false;
+ }
msg_start();
MSG_PUTS(":!");
- msg_outtrans(cmd); /* show what we are doing */
+ msg_outtrans((char_u *) cmd); // show what we are doing
- /* let the shell know if we are redirecting output or not */
- do_shell(cmd, *p_sp != NUL ? kShellOptDoOut : 0);
+ // let the shell know if we are redirecting output or not
+ do_shell((char_u *) cmd, *p_sp != NUL ? kShellOptDoOut : 0);
res = qf_init(wp, fname, (eap->cmdidx != CMD_make
@@ -2546,11 +2548,11 @@ static char_u *get_mef_name(void)
/* Keep trying until the name doesn't exist yet. */
for (;; ) {
- if (start == -1)
- start = os_get_pid();
- else
+ if (start == -1) {
+ start = (int)os_get_pid();
+ } else {
off += 19;
-
+ }
name = xmalloc(STRLEN(p_mef) + 30);
STRCPY(name, p_mef);
sprintf((char *)name + (p - p_mef), "%d%d", start, off);
@@ -2752,8 +2754,8 @@ void ex_cc(exarg_T *eap)
// For cdo and ldo commands, jump to the nth valid error.
// For cfdo and lfdo commands, jump to the nth valid file entry.
- if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
- eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
+ if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
+ || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
size_t n;
if (eap->addr_count > 0) {
assert(eap->line1 >= 0);
@@ -2796,9 +2798,9 @@ void ex_cnext(exarg_T *eap)
}
int errornr;
- if (eap->addr_count > 0 &&
- (eap->cmdidx != CMD_cdo && eap->cmdidx != CMD_ldo &&
- eap->cmdidx != CMD_cfdo && eap->cmdidx != CMD_lfdo)) {
+ if (eap->addr_count > 0
+ && (eap->cmdidx != CMD_cdo && eap->cmdidx != CMD_ldo
+ && eap->cmdidx != CMD_cfdo && eap->cmdidx != CMD_lfdo)) {
errornr = (int)eap->line2;
} else {
errornr = 1;
@@ -2897,7 +2899,7 @@ void ex_vimgrep(exarg_T *eap)
int found_match;
buf_T *first_match_buf = NULL;
time_t seconds = 0;
- int save_mls;
+ long save_mls;
char_u *save_ei = NULL;
aco_save_T aco;
int flags = 0;
@@ -2967,16 +2969,18 @@ void ex_vimgrep(exarg_T *eap)
goto theend;
}
- if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd &&
- eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd)
- || qi->qf_curlist == qi->qf_listcount)
- /* make place for a new list */
+ if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd
+ && eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd)
+ || qi->qf_curlist == qi->qf_listcount) {
+ // make place for a new list
qf_new_list(qi, *eap->cmdlinep);
- else if (qi->qf_lists[qi->qf_curlist].qf_count > 0)
- /* Adding to existing list, find last entry. */
+ } else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) {
+ // Adding to existing list, find last entry.
for (prevp = qi->qf_lists[qi->qf_curlist].qf_start;
- prevp->qf_next != prevp; prevp = prevp->qf_next)
- ;
+ prevp->qf_next != prevp;
+ prevp = prevp->qf_next) {
+ }
+ }
/* parse the list of arguments */
if (get_arglist_exp(p, &fcount, &fnames, true) == FAIL)
@@ -3454,18 +3458,12 @@ int get_errorlist(win_T *wp, list_T *list)
*/
int set_errorlist(win_T *wp, list_T *list, int action, char_u *title)
{
- listitem_T *li;
- dict_T *d;
- char_u *filename, *pattern, *text, *type;
- int bufnum;
- long lnum;
- int col, nr;
- int vcol;
- qfline_T *prevp = NULL;
- int valid, status;
+ listitem_T *li;
+ dict_T *d;
+ qfline_T *prevp = NULL;
int retval = OK;
- qf_info_T *qi = &ql_info;
- int did_bufnr_emsg = FALSE;
+ qf_info_T *qi = &ql_info;
+ bool did_bufnr_emsg = false;
if (wp != NULL) {
qi = ll_get_or_alloc_list(wp);
@@ -3492,21 +3490,22 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title)
if (d == NULL)
continue;
- filename = get_dict_string(d, (char_u *)"filename", TRUE);
- bufnum = get_dict_number(d, (char_u *)"bufnr");
- lnum = get_dict_number(d, (char_u *)"lnum");
- col = get_dict_number(d, (char_u *)"col");
- vcol = get_dict_number(d, (char_u *)"vcol");
- nr = get_dict_number(d, (char_u *)"nr");
- type = get_dict_string(d, (char_u *)"type", TRUE);
- pattern = get_dict_string(d, (char_u *)"pattern", TRUE);
- text = get_dict_string(d, (char_u *)"text", TRUE);
- if (text == NULL)
+ char_u *filename = get_dict_string(d, (char_u *)"filename", true);
+ int bufnum = (int)get_dict_number(d, (char_u *)"bufnr");
+ long lnum = get_dict_number(d, (char_u *)"lnum");
+ int col = (int)get_dict_number(d, (char_u *)"col");
+ char_u vcol = (char_u)get_dict_number(d, (char_u *)"vcol");
+ int nr = (int)get_dict_number(d, (char_u *)"nr");
+ char_u *type = get_dict_string(d, (char_u *)"type", true);
+ char_u *pattern = get_dict_string(d, (char_u *)"pattern", true);
+ char_u *text = get_dict_string(d, (char_u *)"text", true);
+ if (text == NULL) {
text = vim_strsave((char_u *)"");
-
- valid = TRUE;
- if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL))
- valid = FALSE;
+ }
+ bool valid = true;
+ if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) {
+ valid = false;
+ }
/* Mark entries with non-existing buffer number as not valid. Give the
* error message only once. */
@@ -3515,22 +3514,23 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title)
did_bufnr_emsg = TRUE;
EMSGN(_("E92: Buffer %" PRId64 " not found"), bufnum);
}
- valid = FALSE;
+ valid = false;
bufnum = 0;
}
- status = qf_add_entry(qi, &prevp,
- NULL, /* dir */
- filename,
- bufnum,
- text,
- lnum,
- col,
- vcol, /* vis_col */
- pattern, /* search pattern */
- nr,
- type == NULL ? NUL : *type,
- valid);
+ int status = qf_add_entry(qi,
+ &prevp,
+ NULL, // dir
+ filename,
+ bufnum,
+ text,
+ lnum,
+ col,
+ vcol, // vis_col
+ pattern, // search pattern
+ nr,
+ (char_u)(type == NULL ? NUL : *type),
+ valid);
xfree(filename);
xfree(pattern);
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 608aa38466..886a48e7c5 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -3445,13 +3445,14 @@ static long bt_regexec_both(char_u *line,
c = regline[col];
if (prog->regstart == NUL
|| prog->regstart == c
- || (ireg_ic && ((
- (enc_utf8 && utf_fold(prog->regstart) == utf_fold(c)))
- || (c < 255 && prog->regstart < 255 &&
- vim_tolower(prog->regstart) == vim_tolower(c)))))
+ || (ireg_ic
+ && (((enc_utf8 && utf_fold(prog->regstart) == utf_fold(c)))
+ || (c < 255 && prog->regstart < 255
+ && vim_tolower(prog->regstart) == vim_tolower(c))))) {
retval = regtry(prog, col);
- else
+ } else {
retval = 0;
+ }
} else {
int tm_count = 0;
/* Messy cases: unanchored match. */
@@ -4121,15 +4122,15 @@ regmatch (
char_u *opnd;
opnd = OPERAND(scan);
- /* Inline the first byte, for speed. */
+ // Inline the first byte, for speed.
if (*opnd != *reginput
- && (!ireg_ic || (
- !enc_utf8 &&
- vim_tolower(*opnd) != vim_tolower(*reginput))))
+ && (!ireg_ic
+ || (!enc_utf8
+ && vim_tolower(*opnd) != vim_tolower(*reginput)))) {
status = RA_NOMATCH;
- else if (*opnd == NUL) {
- /* match empty string always works; happens when "~" is
- * empty. */
+ } else if (*opnd == NUL) {
+ // match empty string always works; happens when "~" is
+ // empty.
} else {
if (opnd[1] == NUL && !(enc_utf8 && ireg_ic)) {
len = 1; /* matched a single byte above */
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 4020fa6e28..7e53b2ccd1 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -3464,13 +3464,12 @@ static char *pim_info(nfa_pim_T *pim)
#endif
-/* Used during execution: whether a match has been found. */
+// Used during execution: whether a match has been found.
static int nfa_match;
+static proftime_T *nfa_time_limit;
+static int nfa_time_count;
-
-/*
- * Copy postponed invisible match info from "from" to "to".
- */
+// Copy postponed invisible match info from "from" to "to".
static void copy_pim(nfa_pim_T *to, nfa_pim_T *from)
{
to->result = from->result;
@@ -4048,6 +4047,7 @@ skip_add:
sub->list.multi[subidx].start_col =
(colnr_T)(reginput - regline + off);
}
+ sub->list.multi[subidx].end_lnum = -1;
} else {
if (subidx < sub->in_use) {
save_ptr = sub->list.line[subidx].start;
@@ -4565,7 +4565,7 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
if (log_fd != NULL) {
fprintf(log_fd, "****************************\n");
fprintf(log_fd, "FINISHED RUNNING nfa_regmatch() recursively\n");
- fprintf(log_fd, "MATCH = %s\n", result == TRUE ? "OK" : "FALSE");
+ fprintf(log_fd, "MATCH = %s\n", !result ? "FALSE" : "OK");
fprintf(log_fd, "****************************\n");
} else {
EMSG(_(
@@ -4777,8 +4777,8 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
int c2_len = PTR2LEN(s2);
int c2 = PTR2CHAR(s2);
- if ((c1 != c2 && (!ireg_ic || vim_tolower(c1) != vim_tolower(c2))) ||
- c1_len != c2_len) {
+ if ((c1 != c2 && (!ireg_ic || vim_tolower(c1) != vim_tolower(c2)))
+ || c1_len != c2_len) {
match = false;
break;
}
@@ -4812,22 +4812,21 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
#undef PTR2LEN
}
-/*
- * Main matching routine.
- *
- * Run NFA to determine whether it matches reginput.
- *
- * When "nfa_endp" is not NULL it is a required end-of-match position.
- *
- * Return TRUE if there is a match, FALSE otherwise.
- * When there is a match "submatch" contains the positions.
- * Note: Caller must ensure that: start != NULL.
- */
-static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *submatch, regsubs_T *m)
+/// Main matching routine.
+///
+/// Run NFA to determine whether it matches reginput.
+///
+/// When "nfa_endp" is not NULL it is a required end-of-match position.
+///
+/// Return TRUE if there is a match, FALSE otherwise.
+/// When there is a match "submatch" contains the positions.
+/// Note: Caller must ensure that: start != NULL.
+static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
+ regsubs_T *submatch, regsubs_T *m)
{
int result;
int flag = 0;
- int go_to_nextline = FALSE;
+ bool go_to_nextline = false;
nfa_thread_T *t;
nfa_list_T list[2];
int listidx;
@@ -4844,18 +4843,22 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
if (debug == NULL) {
EMSG2(_("(NFA) COULD NOT OPEN %s !"), NFA_REGEXP_DEBUG_LOG);
- return FALSE;
+ return false;
}
#endif
- /* Some patterns may take a long time to match, especially when using
- * recursive_regmatch(). Allow interrupting them with CTRL-C. */
+ // Some patterns may take a long time to match, especially when using
+ // recursive_regmatch(). Allow interrupting them with CTRL-C.
fast_breakcheck();
- if (got_int)
- return FALSE;
+ if (got_int) {
+ return false;
+ }
+ if (nfa_time_limit != NULL && profile_passed_limit(*nfa_time_limit)) {
+ return false;
+ }
- nfa_match = FALSE;
+ nfa_match = false;
- /* Allocate memory for the lists of nodes. */
+ // Allocate memory for the lists of nodes.
size_t size = (nstate + 1) * sizeof(nfa_thread_T);
list[0].t = xmalloc(size);
list[0].len = nstate + 1;
@@ -4923,7 +4926,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
}
if (curc == NUL) {
clen = 0;
- go_to_nextline = FALSE;
+ go_to_nextline = false;
}
/* swap lists */
@@ -5006,7 +5009,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
if (enc_utf8 && !ireg_icombine && utf_iscomposing(curc)) {
break;
}
- nfa_match = TRUE;
+ nfa_match = true;
copy_sub(&submatch->norm, &t->subs.norm);
if (nfa_has_zsubexpr)
copy_sub(&submatch->synt, &t->subs.synt);
@@ -5071,10 +5074,11 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
fprintf(log_fd, "Match found:\n");
log_subsexpr(m);
#endif
- nfa_match = TRUE;
- /* See comment above at "goto nextchar". */
- if (nextlist->n == 0)
+ nfa_match = true;
+ // See comment above at "goto nextchar".
+ if (nextlist->n == 0) {
clen = 0;
+ }
goto nextchar;
case NFA_START_INVISIBLE:
@@ -5091,8 +5095,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
failure_chance(t->state->out, 0),
failure_chance(t->state->out1->out, 0));
#endif
- /* Do it directly if there already is a PIM or when
- * nfa_postprocess() detected it will work better. */
+ // Do it directly if there already is a PIM or when
+ // nfa_postprocess() detected it will work better.
if (t->pim.result != NFA_PIM_UNUSED
|| t->state->c == NFA_START_INVISIBLE_FIRST
|| t->state->c == NFA_START_INVISIBLE_NEG_FIRST
@@ -5100,42 +5104,40 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
|| t->state->c == NFA_START_INVISIBLE_BEFORE_NEG_FIRST) {
int in_use = m->norm.in_use;
- /* Copy submatch info for the recursive call, opposite
- * of what happens on success below. */
+ // Copy submatch info for the recursive call, opposite
+ // of what happens on success below.
copy_sub_off(&m->norm, &t->subs.norm);
if (nfa_has_zsubexpr)
copy_sub_off(&m->synt, &t->subs.synt);
- /*
- * First try matching the invisible match, then what
- * follows.
- */
- result = recursive_regmatch(t->state, NULL, prog,
- submatch, m, &listids);
+ // First try matching the invisible match, then what
+ // follows.
+ result = recursive_regmatch(t->state, NULL, prog, submatch, m,
+ &listids);
if (result == NFA_TOO_EXPENSIVE) {
nfa_match = result;
goto theend;
}
- /* for \@! and \@<! it is a match when the result is
- * FALSE */
+ // for \@! and \@<! it is a match when the result is
+ // FALSE
if (result != (t->state->c == NFA_START_INVISIBLE_NEG
|| t->state->c == NFA_START_INVISIBLE_NEG_FIRST
|| t->state->c
== NFA_START_INVISIBLE_BEFORE_NEG
|| t->state->c
== NFA_START_INVISIBLE_BEFORE_NEG_FIRST)) {
- /* Copy submatch info from the recursive call */
+ // Copy submatch info from the recursive call
copy_sub_off(&t->subs.norm, &m->norm);
if (nfa_has_zsubexpr)
copy_sub_off(&t->subs.synt, &m->synt);
- /* If the pattern has \ze and it matched in the
- * sub pattern, use it. */
+ // If the pattern has \ze and it matched in the
+ // sub pattern, use it.
copy_ze_off(&t->subs.norm, &m->norm);
- /* t->state->out1 is the corresponding
- * END_INVISIBLE node; Add its out to the current
- * list (zero-width match). */
+ // t->state->out1 is the corresponding
+ // END_INVISIBLE node; Add its out to the current
+ // list (zero-width match).
add_here = true;
add_state = t->state->out1->out;
}
@@ -5143,12 +5145,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
} else {
nfa_pim_T pim;
- /*
- * First try matching what follows. Only if a match
- * is found verify the invisible match matches. Add a
- * nfa_pim_T to the following states, it contains info
- * about the invisible match.
- */
+ // First try matching what follows. Only if a match
+ // is found verify the invisible match matches. Add a
+ // nfa_pim_T to the following states, it contains info
+ // about the invisible match.
pim.state = t->state;
pim.result = NFA_PIM_TODO;
pim.subs.norm.in_use = 0;
@@ -5159,9 +5159,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
} else
pim.end.ptr = reginput;
- /* t->state->out1 is the corresponding END_INVISIBLE
- * node; Add its out to the current list (zero-width
- * match). */
+ // t->state->out1 is the corresponding END_INVISIBLE
+ // node; Add its out to the current list (zero-width
+ // match).
addstate_here(thislist, t->state->out1->out, &t->subs,
&pim, &listidx);
}
@@ -5175,8 +5175,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
int skip_lid = 0;
#endif
- /* There is no point in trying to match the pattern if the
- * output state is not going to be added to the list. */
+ // There is no point in trying to match the pattern if the
+ // output state is not going to be added to the list.
if (state_in_list(nextlist, t->state->out1->out, &t->subs)) {
skip = t->state->out1->out;
#ifdef REGEXP_DEBUG
@@ -5205,15 +5205,16 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
#endif
break;
}
- /* Copy submatch info to the recursive call, opposite of what
- * happens afterwards. */
+ // Copy submatch info to the recursive call, opposite of what
+ // happens afterwards.
copy_sub_off(&m->norm, &t->subs.norm);
- if (nfa_has_zsubexpr)
+ if (nfa_has_zsubexpr) {
copy_sub_off(&m->synt, &t->subs.synt);
+ }
- /* First try matching the pattern. */
- result = recursive_regmatch(t->state, NULL, prog,
- submatch, m, &listids);
+ // First try matching the pattern.
+ result = recursive_regmatch(t->state, NULL, prog, submatch, m,
+ &listids);
if (result == NFA_TOO_EXPENSIVE) {
nfa_match = result;
goto theend;
@@ -5225,36 +5226,38 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
fprintf(log_fd, "NFA_START_PATTERN matches:\n");
log_subsexpr(m);
#endif
- /* Copy submatch info from the recursive call */
+ // Copy submatch info from the recursive call
copy_sub_off(&t->subs.norm, &m->norm);
- if (nfa_has_zsubexpr)
+ if (nfa_has_zsubexpr) {
copy_sub_off(&t->subs.synt, &m->synt);
- /* Now we need to skip over the matched text and then
- * continue with what follows. */
- if (REG_MULTI)
- /* TODO: multi-line match */
+ }
+ // Now we need to skip over the matched text and then
+ // continue with what follows.
+ if (REG_MULTI) {
+ // TODO(RE): multi-line match
bytelen = m->norm.list.multi[0].end_col
- (int)(reginput - regline);
- else
+ } else {
bytelen = (int)(m->norm.list.line[0].end - reginput);
+ }
#ifdef REGEXP_DEBUG
fprintf(log_fd, "NFA_START_PATTERN length: %d\n", bytelen);
#endif
if (bytelen == 0) {
- /* empty match, output of corresponding
- * NFA_END_PATTERN/NFA_SKIP to be used at current
- * position */
+ // empty match, output of corresponding
+ // NFA_END_PATTERN/NFA_SKIP to be used at current
+ // position
add_here = true;
add_state = t->state->out1->out->out;
} else if (bytelen <= clen) {
- /* match current character, output of corresponding
- * NFA_END_PATTERN to be used at next position. */
+ // match current character, output of corresponding
+ // NFA_END_PATTERN to be used at next position.
add_state = t->state->out1->out->out;
add_off = clen;
} else {
- /* skip over the matched characters, set character
- * count in NFA_SKIP */
+ // skip over the matched characters, set character
+ // count in NFA_SKIP
add_state = t->state->out1->out;
add_off = bytelen;
add_count = bytelen - clen;
@@ -5278,23 +5281,25 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
break;
case NFA_BOW:
- result = TRUE;
+ result = true;
- if (curc == NUL)
- result = FALSE;
- else if (has_mbyte) {
+ if (curc == NUL) {
+ result = false;
+ } else if (has_mbyte) {
int this_class;
- /* Get class of current and previous char (if it exists). */
+ // Get class of current and previous char (if it exists).
this_class = mb_get_class_buf(reginput, reg_buf);
- if (this_class <= 1)
- result = FALSE;
- else if (reg_prev_class() == this_class)
- result = FALSE;
+ if (this_class <= 1) {
+ result = false;
+ } else if (reg_prev_class() == this_class) {
+ result = false;
+ }
} else if (!vim_iswordc_buf(curc, reg_buf)
|| (reginput > regline
- && vim_iswordc_buf(reginput[-1], reg_buf)))
- result = FALSE;
+ && vim_iswordc_buf(reginput[-1], reg_buf))) {
+ result = false;
+ }
if (result) {
add_here = true;
add_state = t->state->out;
@@ -5302,22 +5307,24 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
break;
case NFA_EOW:
- result = TRUE;
- if (reginput == regline)
- result = FALSE;
- else if (has_mbyte) {
+ result = true;
+ if (reginput == regline) {
+ result = false;
+ } else if (has_mbyte) {
int this_class, prev_class;
- /* Get class of current and previous char (if it exists). */
+ // Get class of current and previous char (if it exists).
this_class = mb_get_class_buf(reginput, reg_buf);
prev_class = reg_prev_class();
if (this_class == prev_class
- || prev_class == 0 || prev_class == 1)
- result = FALSE;
+ || prev_class == 0 || prev_class == 1) {
+ result = false;
+ }
} else if (!vim_iswordc_buf(reginput[-1], reg_buf)
|| (reginput[0] != NUL
- && vim_iswordc_buf(curc, reg_buf)))
- result = FALSE;
+ && vim_iswordc_buf(curc, reg_buf))) {
+ result = false;
+ }
if (result) {
add_here = true;
add_state = t->state->out;
@@ -5352,30 +5359,31 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
sta = t->state->out;
len = 0;
if (utf_iscomposing(sta->c)) {
- /* Only match composing character(s), ignore base
- * character. Used for ".{composing}" and "{composing}"
- * (no preceding character). */
+ // Only match composing character(s), ignore base
+ // character. Used for ".{composing}" and "{composing}"
+ // (no preceding character).
len += mb_char2len(mc);
}
if (ireg_icombine && len == 0) {
- /* If \Z was present, then ignore composing characters.
- * When ignoring the base character this always matches. */
- if (sta->c != curc)
+ // If \Z was present, then ignore composing characters.
+ // When ignoring the base character this always matches.
+ if (sta->c != curc) {
result = FAIL;
- else
+ } else {
result = OK;
- while (sta->c != NFA_END_COMPOSING)
+ }
+ while (sta->c != NFA_END_COMPOSING) {
sta = sta->out;
- }
- /* Check base character matches first, unless ignored. */
- else if (len > 0 || mc == sta->c) {
+ }
+ } else if (len > 0 || mc == sta->c) {
+ // Check base character matches first, unless ignored.
if (len == 0) {
len += mb_char2len(mc);
sta = sta->out;
}
- /* We don't care about the order of composing characters.
- * Get them into cchars[] first. */
+ // We don't care about the order of composing characters.
+ // Get them into cchars[] first.
while (len < clen) {
mc = mb_ptr2char(reginput + len);
cchars[ccount++] = mc;
@@ -5384,9 +5392,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
break;
}
- /* Check that each composing char in the pattern matches a
- * composing char in the text. We do not check if all
- * composing chars are matched. */
+ // Check that each composing char in the pattern matches a
+ // composing char in the text. We do not check if all
+ // composing chars are matched.
result = OK;
while (sta->c != NFA_END_COMPOSING) {
for (j = 0; j < ccount; ++j)
@@ -5401,7 +5409,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
} else
result = FAIL;
- end = t->state->out1; /* NFA_END_COMPOSING */
+ end = t->state->out1; // NFA_END_COMPOSING
ADD_STATE_IF_MATCH(end);
break;
}
@@ -5409,13 +5417,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
case NFA_NEWL:
if (curc == NUL && !reg_line_lbr && REG_MULTI
&& reglnum <= reg_maxline) {
- go_to_nextline = TRUE;
- /* Pass -1 for the offset, which means taking the position
- * at the start of the next line. */
+ go_to_nextline = true;
+ // Pass -1 for the offset, which means taking the position
+ // at the start of the next line.
add_state = t->state->out;
add_off = -1;
} else if (curc == '\n' && reg_line_lbr) {
- /* match \n as if it is an ordinary character */
+ // match \n as if it is an ordinary character
add_state = t->state->out;
add_off = 1;
}
@@ -5424,16 +5432,17 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
case NFA_START_COLL:
case NFA_START_NEG_COLL:
{
- /* What follows is a list of characters, until NFA_END_COLL.
- * One of them must match or none of them must match. */
+ // What follows is a list of characters, until NFA_END_COLL.
+ // One of them must match or none of them must match.
nfa_state_T *state;
int result_if_matched;
int c1, c2;
- /* Never match EOL. If it's part of the collection it is added
- * as a separate state with an OR. */
- if (curc == NUL)
+ // Never match EOL. If it's part of the collection it is added
+ // as a separate state with an OR.
+ if (curc == NUL) {
break;
+ }
state = t->state->out;
result_if_matched = (t->state->c == NFA_START_COLL);
@@ -5444,7 +5453,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
}
if (state->c == NFA_RANGE_MIN) {
c1 = state->val;
- state = state->out; /* advance to NFA_RANGE_MAX */
+ state = state->out; // advance to NFA_RANGE_MAX
c2 = state->val;
#ifdef REGEXP_DEBUG
fprintf(log_fd, "NFA_RANGE_MIN curc=%d c1=%d c2=%d\n",
@@ -5477,8 +5486,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
state = state->out;
}
if (result) {
- /* next state is in out of the NFA_END_COLL, out1 of
- * START points to the END state */
+ // next state is in out of the NFA_END_COLL, out1 of
+ // START points to the END state
add_state = t->state->out1->out;
add_off = clen;
}
@@ -5486,7 +5495,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
}
case NFA_ANY:
- /* Any char except '\0', (end of input) does not match. */
+ // Any char except '\0', (end of input) does not match.
if (curc > 0) {
add_state = t->state->out;
add_off = clen;
@@ -5505,157 +5514,155 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
add_state = t->state->out;
break;
- /*
- * Character classes like \a for alpha, \d for digit etc.
- */
- case NFA_IDENT: /* \i */
+ // Character classes like \a for alpha, \d for digit etc.
+ case NFA_IDENT: // \i
result = vim_isIDc(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_SIDENT: /* \I */
+ case NFA_SIDENT: // \I
result = !ascii_isdigit(curc) && vim_isIDc(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_KWORD: /* \k */
+ case NFA_KWORD: // \k
result = vim_iswordp_buf(reginput, reg_buf);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_SKWORD: /* \K */
+ case NFA_SKWORD: // \K
result = !ascii_isdigit(curc)
&& vim_iswordp_buf(reginput, reg_buf);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_FNAME: /* \f */
+ case NFA_FNAME: // \f
result = vim_isfilec(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_SFNAME: /* \F */
+ case NFA_SFNAME: // \F
result = !ascii_isdigit(curc) && vim_isfilec(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_PRINT: /* \p */
+ case NFA_PRINT: // \p
result = vim_isprintc(PTR2CHAR(reginput));
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_SPRINT: /* \P */
+ case NFA_SPRINT: // \P
result = !ascii_isdigit(curc) && vim_isprintc(PTR2CHAR(reginput));
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_WHITE: /* \s */
+ case NFA_WHITE: // \s
result = ascii_iswhite(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_NWHITE: /* \S */
+ case NFA_NWHITE: // \S
result = curc != NUL && !ascii_iswhite(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_DIGIT: /* \d */
+ case NFA_DIGIT: // \d
result = ri_digit(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_NDIGIT: /* \D */
+ case NFA_NDIGIT: // \D
result = curc != NUL && !ri_digit(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_HEX: /* \x */
+ case NFA_HEX: // \x
result = ri_hex(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_NHEX: /* \X */
+ case NFA_NHEX: // \X
result = curc != NUL && !ri_hex(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_OCTAL: /* \o */
+ case NFA_OCTAL: // \o
result = ri_octal(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_NOCTAL: /* \O */
+ case NFA_NOCTAL: // \O
result = curc != NUL && !ri_octal(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_WORD: /* \w */
+ case NFA_WORD: // \w
result = ri_word(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_NWORD: /* \W */
+ case NFA_NWORD: // \W
result = curc != NUL && !ri_word(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_HEAD: /* \h */
+ case NFA_HEAD: // \h
result = ri_head(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_NHEAD: /* \H */
+ case NFA_NHEAD: // \H
result = curc != NUL && !ri_head(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_ALPHA: /* \a */
+ case NFA_ALPHA: // \a
result = ri_alpha(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_NALPHA: /* \A */
+ case NFA_NALPHA: // \A
result = curc != NUL && !ri_alpha(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_LOWER: /* \l */
+ case NFA_LOWER: // \l
result = ri_lower(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_NLOWER: /* \L */
+ case NFA_NLOWER: // \L
result = curc != NUL && !ri_lower(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_UPPER: /* \u */
+ case NFA_UPPER: // \u
result = ri_upper(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_NUPPER: /* \U */
+ case NFA_NUPPER: // \U
result = curc != NUL && !ri_upper(curc);
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_LOWER_IC: /* [a-z] */
+ case NFA_LOWER_IC: // [a-z]
result = ri_lower(curc) || (ireg_ic && ri_upper(curc));
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_NLOWER_IC: /* [^a-z] */
+ case NFA_NLOWER_IC: // [^a-z]
result = curc != NUL
&& !(ri_lower(curc) || (ireg_ic && ri_upper(curc)));
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_UPPER_IC: /* [A-Z] */
+ case NFA_UPPER_IC: // [A-Z]
result = ri_upper(curc) || (ireg_ic && ri_lower(curc));
ADD_STATE_IF_MATCH(t->state);
break;
- case NFA_NUPPER_IC: /* ^[A-Z] */
+ case NFA_NUPPER_IC: // [^A-Z]
result = curc != NUL
&& !(ri_upper(curc) || (ireg_ic && ri_lower(curc)));
ADD_STATE_IF_MATCH(t->state);
@@ -5679,7 +5686,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
case NFA_ZREF7:
case NFA_ZREF8:
case NFA_ZREF9:
- /* \1 .. \9 \z1 .. \z9 */
+ // \1 .. \9 \z1 .. \z9
{
int subidx;
int bytelen;
@@ -5694,18 +5701,18 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
if (result) {
if (bytelen == 0) {
- /* empty match always works, output of NFA_SKIP to be
- * used next */
+ // empty match always works, output of NFA_SKIP to be
+ // used next
add_here = true;
add_state = t->state->out->out;
} else if (bytelen <= clen) {
- /* match current character, jump ahead to out of
- * NFA_SKIP */
+ // match current character, jump ahead to out of
+ // NFA_SKIP
add_state = t->state->out->out;
add_off = clen;
} else {
- /* skip over the matched characters, set character
- * count in NFA_SKIP */
+ // skip over the matched characters, set character
+ // count in NFA_SKIP
add_state = t->state->out;
add_off = bytelen;
add_count = bytelen - clen;
@@ -5714,13 +5721,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
break;
}
case NFA_SKIP:
- /* character of previous matching \1 .. \9 or \@> */
+ // character of previous matching \1 .. \9 or \@>
if (t->count - clen <= 0) {
- /* end of match, go to what follows */
+ // end of match, go to what follows
add_state = t->state->out;
add_off = clen;
} else {
- /* add state again with decremented count */
+ // add state again with decremented count
add_state = t->state;
add_off = 0;
add_count = t->count - clen;
@@ -5772,7 +5779,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
break;
}
- result = FALSE;
+ result = false;
win_T *wp = reg_win == NULL ? curwin : reg_win;
if (op == 1 && col - 1 > t->state->val && col > 100) {
long ts = wp->w_buffer->b_p_ts;
@@ -5802,9 +5809,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
{
pos_T *pos = getmark_buf(reg_buf, t->state->val, FALSE);
- /* Compare the mark position to the match position. */
- result = (pos != NULL /* mark doesn't exist */
- && pos->lnum > 0 /* mark isn't set in reg_buf */
+ // Compare the mark position to the match position.
+ result = (pos != NULL // mark doesn't exist
+ && pos->lnum > 0 // mark isn't set in reg_buf
&& (pos->lnum == reglnum + reg_firstlnum
? (pos->col == (colnr_T)(reginput - regline)
? t->state->c == NFA_MARK
@@ -5861,11 +5868,11 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
case NFA_ZOPEN9:
case NFA_NOPEN:
case NFA_ZSTART:
- /* These states are only added to be able to bail out when
- * they are added again, nothing is to be done. */
+ // These states are only added to be able to bail out when
+ // they are added again, nothing is to be done.
break;
- default: /* regular character */
+ default: // regular character
{
int c = t->state->c;
@@ -5887,8 +5894,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
ADD_STATE_IF_MATCH(t->state);
break;
}
-
- } /* switch (t->state->c) */
+ } // switch (t->state->c)
if (add_state != NULL) {
nfa_pim_T *pim;
@@ -5899,8 +5905,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
else
pim = &t->pim;
- /* Handle the postponed invisible match if the match might end
- * without advancing and before the end of the line. */
+ // Handle the postponed invisible match if the match might end
+ // without advancing and before the end of the line.
if (pim != NULL && (clen == 0 || match_follows(add_state, 0))) {
if (pim->result == NFA_PIM_TODO) {
#ifdef REGEXP_DEBUG
@@ -5912,15 +5918,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
result = recursive_regmatch(pim->state, pim,
prog, submatch, m, &listids);
pim->result = result ? NFA_PIM_MATCH : NFA_PIM_NOMATCH;
- /* for \@! and \@<! it is a match when the result is
- * FALSE */
+ // for \@! and \@<! it is a match when the result is
+ // FALSE
if (result != (pim->state->c == NFA_START_INVISIBLE_NEG
|| pim->state->c == NFA_START_INVISIBLE_NEG_FIRST
|| pim->state->c
== NFA_START_INVISIBLE_BEFORE_NEG
|| pim->state->c
== NFA_START_INVISIBLE_BEFORE_NEG_FIRST)) {
- /* Copy submatch info from the recursive call */
+ // Copy submatch info from the recursive call
copy_sub_off(&pim->subs.norm, &m->norm);
if (nfa_has_zsubexpr)
copy_sub_off(&pim->subs.synt, &m->synt);
@@ -5933,34 +5939,35 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
log_fd,
"Using previous recursive nfa_regmatch() result, result == %d\n",
pim->result);
- fprintf(log_fd, "MATCH = %s\n", result == TRUE ? "OK" : "FALSE");
+ fprintf(log_fd, "MATCH = %s\n", result ? "OK" : "FALSE");
fprintf(log_fd, "\n");
#endif
}
- /* for \@! and \@<! it is a match when result is FALSE */
+ // for \@! and \@<! it is a match when result is FALSE
if (result != (pim->state->c == NFA_START_INVISIBLE_NEG
|| pim->state->c == NFA_START_INVISIBLE_NEG_FIRST
|| pim->state->c
== NFA_START_INVISIBLE_BEFORE_NEG
|| pim->state->c
== NFA_START_INVISIBLE_BEFORE_NEG_FIRST)) {
- /* Copy submatch info from the recursive call */
+ // Copy submatch info from the recursive call
copy_sub_off(&t->subs.norm, &pim->subs.norm);
if (nfa_has_zsubexpr)
copy_sub_off(&t->subs.synt, &pim->subs.synt);
- } else
- /* look-behind match failed, don't add the state */
+ } else {
+ // look-behind match failed, don't add the state
continue;
+ }
- /* Postponed invisible match was handled, don't add it to
- * following states. */
+ // Postponed invisible match was handled, don't add it to
+ // following states.
pim = NULL;
}
- /* If "pim" points into l->t it will become invalid when
- * adding the state causes the list to be reallocated. Make a
- * local copy to avoid that. */
+ // If "pim" points into l->t it will become invalid when
+ // adding the state causes the list to be reallocated. Make a
+ // local copy to avoid that.
if (pim == &t->pim) {
copy_pim(&pim_copy, pim);
pim = &pim_copy;
@@ -5974,18 +5981,17 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
nextlist->t[nextlist->n - 1].count = add_count;
}
}
-
- } /* for (thislist = thislist; thislist->state; thislist++) */
-
- /* Look for the start of a match in the current position by adding the
- * start state to the list of states.
- * The first found match is the leftmost one, thus the order of states
- * matters!
- * Do not add the start state in recursive calls of nfa_regmatch(),
- * because recursive calls should only start in the first position.
- * Unless "nfa_endp" is not NULL, then we match the end position.
- * Also don't start a match past the first line. */
- if (nfa_match == FALSE
+ } // for (thislist = thislist; thislist->state; thislist++)
+
+ // Look for the start of a match in the current position by adding the
+ // start state to the list of states.
+ // The first found match is the leftmost one, thus the order of states
+ // matters!
+ // Do not add the start state in recursive calls of nfa_regmatch(),
+ // because recursive calls should only start in the first position.
+ // Unless "nfa_endp" is not NULL, then we match the end position.
+ // Also don't start a match past the first line.
+ if (!nfa_match
&& ((toplevel
&& reglnum == 0
&& clen != 0
@@ -6001,8 +6007,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
#ifdef REGEXP_DEBUG
fprintf(log_fd, "(---) STARTSTATE\n");
#endif
- /* Inline optimized code for addstate() if we know the state is
- * the first MOPEN. */
+ // Inline optimized code for addstate() if we know the state is
+ // the first MOPEN.
if (toplevel) {
int add = TRUE;
int c;
@@ -6011,18 +6017,19 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
if (nextlist->n == 0) {
colnr_T col = (colnr_T)(reginput - regline) + clen;
- /* Nextlist is empty, we can skip ahead to the
- * character that must appear at the start. */
- if (skip_to_start(prog->regstart, &col) == FAIL)
+ // Nextlist is empty, we can skip ahead to the
+ // character that must appear at the start.
+ if (skip_to_start(prog->regstart, &col) == FAIL) {
break;
+ }
#ifdef REGEXP_DEBUG
fprintf(log_fd, " Skipping ahead %d bytes to regstart\n",
col - ((colnr_T)(reginput - regline) + clen));
#endif
reginput = regline + col - clen;
} else {
- /* Checking if the required start character matches is
- * cheaper than adding a state that won't match. */
+ // Checking if the required start character matches is
+ // cheaper than adding a state that won't match.
c = PTR2CHAR(reginput + clen);
if (c != prog->regstart && (!ireg_ic || vim_tolower(c)
!= vim_tolower(prog->regstart))) {
@@ -6059,21 +6066,29 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
#endif
nextchar:
- /* Advance to the next character, or advance to the next line, or
- * finish. */
- if (clen != 0)
+ // Advance to the next character, or advance to the next line, or
+ // finish.
+ if (clen != 0) {
reginput += clen;
- else if (go_to_nextline || (nfa_endp != NULL && REG_MULTI
- && reglnum < nfa_endp->se_u.pos.lnum))
+ } else if (go_to_nextline || (nfa_endp != NULL && REG_MULTI
+ && reglnum < nfa_endp->se_u.pos.lnum)) {
reg_nextline();
- else
+ } else {
break;
+ }
// Allow interrupting with CTRL-C.
- fast_breakcheck();
+ line_breakcheck();
if (got_int) {
break;
}
+ // Check for timeout once every twenty times to avoid overhead.
+ if (nfa_time_limit != NULL && ++nfa_time_count == 20) {
+ nfa_time_count = 0;
+ if (profile_passed_limit(*nfa_time_limit)) {
+ break;
+ }
+ }
}
#ifdef REGEXP_DEBUG
@@ -6083,7 +6098,7 @@ nextchar:
#endif
theend:
- /* Free memory */
+ // Free memory
xfree(list[0].t);
xfree(list[1].t);
xfree(listids);
@@ -6095,11 +6110,9 @@ theend:
return nfa_match;
}
-/*
- * Try match of "prog" with at regline["col"].
- * Returns <= 0 for failure, number of lines contained in the match otherwise.
- */
-static long nfa_regtry(nfa_regprog_T *prog, colnr_T col)
+// Try match of "prog" with at regline["col"].
+// Returns <= 0 for failure, number of lines contained in the match otherwise.
+static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm)
{
int i;
regsubs_T subs, m;
@@ -6109,6 +6122,8 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col)
#endif
reginput = regline + col;
+ nfa_time_limit = tm;
+ nfa_time_count = 0;
#ifdef REGEXP_DEBUG
f = fopen(NFA_REGEXP_RUN_LOG, "a");
@@ -6133,7 +6148,7 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col)
clear_sub(&m.synt);
int result = nfa_regmatch(prog, start, &subs, &m);
- if (result == FALSE) {
+ if (!result) {
return 0;
} else if (result == NFA_TOO_EXPENSIVE) {
return result;
@@ -6179,7 +6194,8 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col)
if (prog->reghasz == REX_SET) {
cleanup_zsubexpr();
re_extmatch_out = make_extmatch();
- for (i = 0; i < subs.synt.in_use; i++) {
+ // Loop over \z1, \z2, etc. There is no \z0.
+ for (i = 1; i < subs.synt.in_use; i++) {
if (REG_MULTI) {
struct multipos *mpos = &subs.synt.list.multi[i];
@@ -6205,17 +6221,16 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col)
return 1 + reglnum;
}
-/*
- * Match a regexp against a string ("line" points to the string) or multiple
- * lines ("line" is NULL, use reg_getline()).
- *
- * Returns <= 0 for failure, number of lines contained in the match otherwise.
- */
-static long
-nfa_regexec_both (
- char_u *line,
- colnr_T startcol /* column to start looking for match */
-)
+/// Match a regexp against a string ("line" points to the string) or multiple
+/// lines ("line" is NULL, use reg_getline()).
+///
+/// @param line String in which to search or NULL
+/// @param startcol Column to start looking for match
+/// @param tm Timeout limit or NULL
+///
+/// @return <= 0 if there is no match and number of lines contained in the
+/// match otherwise.
+static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm)
{
nfa_regprog_T *prog;
long retval = 0L;
@@ -6295,7 +6310,7 @@ nfa_regexec_both (
prog->state[i].lastlist[1] = 0;
}
- retval = nfa_regtry(prog, col);
+ retval = nfa_regtry(prog, col, tm);
nfa_regengine.expr = NULL;
@@ -6447,7 +6462,7 @@ nfa_regexec_nl (
ireg_ic = rmp->rm_ic;
ireg_icombine = FALSE;
ireg_maxcol = 0;
- return nfa_regexec_both(line, col);
+ return nfa_regexec_both(line, col, NULL);
}
/// Matches a regexp against multiple lines.
@@ -6498,5 +6513,5 @@ static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
ireg_icombine = FALSE;
ireg_maxcol = rmp->rmm_maxcol;
- return nfa_regexec_both(NULL, col);
+ return nfa_regexec_both(NULL, col, tm);
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index e036c49be4..34eef83164 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -387,14 +387,15 @@ void update_screen(int type)
if (wp->w_buffer->b_mod_set) {
win_T *wwp;
- /* Check if we already did this buffer. */
- for (wwp = firstwin; wwp != wp; wwp = wwp->w_next)
- if (wwp->w_buffer == wp->w_buffer)
+ // Check if we already did this buffer.
+ for (wwp = firstwin; wwp != wp; wwp = wwp->w_next) {
+ if (wwp->w_buffer == wp->w_buffer) {
break;
- if (
- wwp == wp &&
- syntax_present(wp))
+ }
+ }
+ if (wwp == wp && syntax_present(wp)) {
syn_stack_apply_changes(wp->w_buffer);
+ }
}
}
@@ -1231,16 +1232,16 @@ static void win_update(win_T *wp)
|| did_update == DID_FOLD
|| (did_update == DID_LINE
&& syntax_present(wp)
- && (
- (foldmethodIsSyntax(wp)
- && hasAnyFolding(wp)) ||
- syntax_check_changed(lnum)))
+ && ((foldmethodIsSyntax(wp)
+ && hasAnyFolding(wp))
+ || syntax_check_changed(lnum)))
// match in fixed position might need redraw
// if lines were inserted or deleted
- || (wp->w_match_head != NULL && buf->b_mod_xlines != 0)
- ))))) {
- if (lnum == mod_top)
- top_to_mod = FALSE;
+ || (wp->w_match_head != NULL
+ && buf->b_mod_xlines != 0)))))) {
+ if (lnum == mod_top) {
+ top_to_mod = false;
+ }
/*
* When at start of changed lines: May scroll following lines
@@ -2184,6 +2185,10 @@ win_line (
int prev_c1 = 0; /* first composing char for prev_c */
int did_line_attr = 0;
+ bool has_bufhl = false; // this buffer has highlight matches
+ int bufhl_attr = 0; // attributes desired by bufhl
+ bufhl_lineinfo_T bufhl_info; // bufhl data for this line
+
/* draw_state: items that are drawn in sequence: */
#define WL_START 0 /* nothing done yet */
# define WL_CMDLINE WL_START + 1 /* cmdline window column */
@@ -2199,11 +2204,13 @@ win_line (
int syntax_seqnr = 0;
int prev_syntax_id = 0;
int conceal_attr = hl_attr(HLF_CONCEAL);
- int is_concealing = FALSE;
- int boguscols = 0; /* nonexistent columns added to force
- wrapping */
- int vcol_off = 0; /* offset for concealed characters */
- int did_wcol = FALSE;
+ int is_concealing = false;
+ int boguscols = 0; ///< nonexistent columns added to
+ ///< force wrapping
+ int vcol_off = 0; ///< offset for concealed characters
+ int did_wcol = false;
+ int match_conc = false; ///< cchar for match functions
+ int has_match_conc = false; ///< match wants to conceal
int old_boguscols = 0;
# define VCOL_HLC (vcol - vcol_off)
# define FIX_FOR_BOGUSCOLS \
@@ -2242,6 +2249,11 @@ win_line (
}
}
+ if (bufhl_start_line(wp->w_buffer, lnum, &bufhl_info)) {
+ has_bufhl = true;
+ extra_check = true;
+ }
+
/* Check for columns to display for 'colorcolumn'. */
color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols;
if (color_cols != NULL)
@@ -2430,13 +2442,18 @@ win_line (
}
}
- /* find start of trailing whitespace */
- if (wp->w_p_list && lcs_trail) {
- trailcol = (colnr_T)STRLEN(ptr);
- while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1]))
- --trailcol;
- trailcol += (colnr_T) (ptr - line);
- extra_check = TRUE;
+ if (wp->w_p_list) {
+ if (lcs_space || lcs_trail) {
+ extra_check = true;
+ }
+ // find start of trailing whitespace
+ if (lcs_trail) {
+ trailcol = (colnr_T)STRLEN(ptr);
+ while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) {
+ trailcol--;
+ }
+ trailcol += (colnr_T) (ptr - line);
+ }
}
/*
@@ -2456,21 +2473,16 @@ win_line (
mb_ptr_adv(ptr);
}
- /* When:
- * - 'cuc' is set, or
- * - 'colorcolumn' is set, or
- * - 'virtualedit' is set, or
- * - the visual mode is active,
- * the end of the line may be before the start of the displayed part.
- */
- if (vcol < v && (
- wp->w_p_cuc
- || draw_color_col
- ||
- virtual_active()
- ||
- (VIsual_active && wp->w_buffer == curwin->w_buffer)
- )) {
+ // When:
+ // - 'cuc' is set, or
+ // - 'colorcolumn' is set, or
+ // - 'virtualedit' is set, or
+ // - the visual mode is active,
+ // the end of the line may be before the start of the displayed part.
+ if (vcol < v && (wp->w_p_cuc
+ || draw_color_col
+ || virtual_active()
+ || (VIsual_active && wp->w_buffer == curwin->w_buffer))) {
vcol = v;
}
@@ -2633,11 +2645,10 @@ win_line (
extra_check = true;
}
- /*
- * Repeat for the whole displayed line.
- */
+ // Repeat for the whole displayed line.
for (;; ) {
- /* Skip this quickly when working on the text. */
+ has_match_conc = false;
+ // Skip this quickly when working on the text.
if (draw_state != WL_LINE) {
if (draw_state == WL_CMDLINE - 1 && n_extra == 0) {
draw_state = WL_CMDLINE;
@@ -2884,8 +2895,16 @@ win_line (
shl->endcol = tmp_col;
}
shl->attr_cur = shl->attr;
+ if (cur != NULL && syn_name2id((char_u *)"Conceal")
+ == cur->hlg_id) {
+ has_match_conc = true;
+ match_conc = cur->conceal_char;
+ } else {
+ has_match_conc = match_conc = false;
+ }
} else if (v == (long)shl->endcol) {
shl->attr_cur = 0;
+ prev_syntax_id = 0;
next_search_hl(wp, shl, lnum, (colnr_T)v, cur);
pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
@@ -3201,27 +3220,7 @@ win_line (
}
}
- ++ptr;
-
- // 'list': change char 160 to lcs_nbsp and space to lcs_space.
- if (wp->w_p_list
- && (((c == 160 || (mb_utf8 && (mb_c == 160 || mb_c == 0x202f)))
- && lcs_nbsp)
- || (c == ' ' && lcs_space && ptr - line <= trailcol))) {
- c = (c == ' ') ? lcs_space : lcs_nbsp;
- if (area_attr == 0 && search_attr == 0) {
- n_attr = 1;
- extra_attr = hl_attr(HLF_8);
- saved_attr2 = char_attr; /* save current attr */
- }
- mb_c = c;
- if (enc_utf8 && (*mb_char2len)(c) > 1) {
- mb_utf8 = TRUE;
- u8cc[0] = 0;
- c = 0xc0;
- } else
- mb_utf8 = FALSE;
- }
+ ptr++;
if (extra_check) {
bool can_spell = true;
@@ -3270,12 +3269,12 @@ win_line (
* contains the @Spell cluster. */
if (has_spell && v >= word_end && v > cur_checked_col) {
spell_attr = 0;
- if (!attr_pri)
+ if (!attr_pri) {
char_attr = syntax_attr;
- if (c != 0 && (
- !has_syntax ||
- can_spell)) {
- char_u *prev_ptr, *p;
+ }
+ if (c != 0 && (!has_syntax || can_spell)) {
+ char_u *prev_ptr;
+ char_u *p;
int len;
hlf_T spell_hlf = HLF_COUNT;
if (has_mbyte) {
@@ -3341,6 +3340,17 @@ win_line (
char_attr = hl_combine_attr(spell_attr, char_attr);
}
+ if (has_bufhl && v > 0) {
+ bufhl_attr = bufhl_get_attr(&bufhl_info, (colnr_T)v);
+ if (bufhl_attr != 0) {
+ if (!attr_pri) {
+ char_attr = hl_combine_attr(char_attr, bufhl_attr);
+ } else {
+ char_attr = hl_combine_attr(bufhl_attr, char_attr);
+ }
+ }
+ }
+
if (wp->w_buffer->terminal) {
char_attr = hl_combine_attr(char_attr, term_attrs[vcol]);
}
@@ -3368,13 +3378,31 @@ win_line (
}
}
+ // 'list': change char 160 to lcs_nbsp and space to lcs_space.
+ if (wp->w_p_list
+ && (((c == 160
+ || (mb_utf8 && (mb_c == 160 || mb_c == 0x202f)))
+ && lcs_nbsp)
+ || (c == ' ' && lcs_space && ptr - line <= trailcol))) {
+ c = (c == ' ') ? lcs_space : lcs_nbsp;
+ n_attr = 1;
+ extra_attr = hl_attr(HLF_8);
+ saved_attr2 = char_attr; // save current attr
+ mb_c = c;
+ if (enc_utf8 && (*mb_char2len)(c) > 1) {
+ mb_utf8 = true;
+ u8cc[0] = 0;
+ c = 0xc0;
+ } else {
+ mb_utf8 = false;
+ }
+ }
+
if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') {
c = lcs_trail;
- if (!attr_pri) {
- n_attr = 1;
- extra_attr = hl_attr(HLF_8);
- saved_attr2 = char_attr; /* save current attr */
- }
+ n_attr = 1;
+ extra_attr = hl_attr(HLF_8);
+ saved_attr2 = char_attr; // save current attr
mb_c = c;
if (enc_utf8 && (*mb_char2len)(c) > 1) {
mb_utf8 = TRUE;
@@ -3522,11 +3550,9 @@ win_line (
c = ' ';
}
lcs_eol_one = -1;
- --ptr; /* put it back at the NUL */
- if (!attr_pri) {
- extra_attr = hl_attr(HLF_AT);
- n_attr = 1;
- }
+ ptr--; // put it back at the NUL
+ extra_attr = hl_attr(HLF_AT);
+ n_attr = 1;
mb_c = c;
if (enc_utf8 && (*mb_char2len)(c) > 1) {
mb_utf8 = TRUE;
@@ -3555,12 +3581,10 @@ win_line (
n_extra = byte2cells(c) - 1;
c = *p_extra++;
}
- if (!attr_pri) {
- n_attr = n_extra + 1;
- extra_attr = hl_attr(HLF_8);
- saved_attr2 = char_attr; /* save current attr */
- }
- mb_utf8 = FALSE; /* don't draw as UTF-8 */
+ n_attr = n_extra + 1;
+ extra_attr = hl_attr(HLF_8);
+ saved_attr2 = char_attr; // save current attr
+ mb_utf8 = false; // don't draw as UTF-8
} else if (VIsual_active
&& (VIsual_mode == Ctrl_V
|| VIsual_mode == 'v')
@@ -3571,25 +3595,22 @@ win_line (
wp->w_p_rl ? (col >= 0) :
(col < wp->w_width))) {
c = ' ';
- --ptr; /* put it back at the NUL */
- } else if ((
- diff_hlf != (hlf_T)0 ||
- line_attr != 0
- ) && (
- wp->w_p_rl ? (col >= 0) :
- (col
- - boguscols
- < wp->w_width))) {
- /* Highlight until the right side of the window */
+ ptr--; // put it back at the NUL
+ } else if ((diff_hlf != (hlf_T)0 || line_attr != 0)
+ && (wp->w_p_rl
+ ? (col >= 0)
+ : (col - boguscols < wp->w_width))) {
+ // Highlight until the right side of the window
c = ' ';
- --ptr; /* put it back at the NUL */
+ ptr--; // put it back at the NUL
- /* Remember we do the char for line highlighting. */
- ++did_line_attr;
+ // Remember we do the char for line highlighting.
+ did_line_attr++;
- /* don't do search HL for the rest of the line */
- if (line_attr != 0 && char_attr == search_attr && col > 0)
+ // don't do search HL for the rest of the line
+ if (line_attr != 0 && char_attr == search_attr && col > 0) {
char_attr = line_attr;
+ }
if (diff_hlf == HLF_TXD) {
diff_hlf = HLF_CHD;
if (attr == 0 || char_attr != attr) {
@@ -3602,24 +3623,28 @@ win_line (
}
}
- if ( wp->w_p_cole > 0
- && (wp != curwin || lnum != wp->w_cursor.lnum ||
- conceal_cursor_line(wp))
- && (syntax_flags & HL_CONCEAL) != 0
- && !(lnum_in_visual_area
- && vim_strchr(wp->w_p_cocu, 'v') == NULL)) {
+ if (wp->w_p_cole > 0
+ && (wp != curwin || lnum != wp->w_cursor.lnum
+ || conceal_cursor_line(wp))
+ && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc)
+ && !(lnum_in_visual_area
+ && vim_strchr(wp->w_p_cocu, 'v') == NULL)) {
char_attr = conceal_attr;
if (prev_syntax_id != syntax_seqnr
- && (syn_get_sub_char() != NUL || wp->w_p_cole == 1)
+ && (syn_get_sub_char() != NUL || match_conc
+ || wp->w_p_cole == 1)
&& wp->w_p_cole != 3) {
- /* First time at this concealed item: display one
- * character. */
- if (syn_get_sub_char() != NUL)
+ // First time at this concealed item: display one
+ // character.
+ if (match_conc) {
+ c = match_conc;
+ } else if (syn_get_sub_char() != NUL) {
c = syn_get_sub_char();
- else if (lcs_conceal != NUL)
+ } else if (lcs_conceal != NUL) {
c = lcs_conceal;
- else
+ } else {
c = ' ';
+ }
prev_syntax_id = syntax_seqnr;
@@ -3660,16 +3685,19 @@ win_line (
&& wp == curwin && lnum == wp->w_cursor.lnum
&& conceal_cursor_line(wp)
&& (int)wp->w_virtcol <= vcol + n_skip) {
- wp->w_wcol = col - boguscols;
+ if (wp->w_p_rl) {
+ wp->w_wcol = wp->w_width - col + boguscols - 1;
+ } else {
+ wp->w_wcol = col - boguscols;
+ }
wp->w_wrow = row;
- did_wcol = TRUE;
+ did_wcol = true;
}
- /* Don't override visual selection highlighting. */
- if (n_attr > 0
- && draw_state == WL_LINE
- && !attr_pri)
- char_attr = extra_attr;
+ // Don't override visual selection highlighting.
+ if (n_attr > 0 && draw_state == WL_LINE) {
+ char_attr = hl_combine_attr(char_attr, extra_attr);
+ }
/*
* Handle the case where we are in column 0 but not on the first
@@ -3697,13 +3725,12 @@ win_line (
mb_utf8 = TRUE;
u8cc[0] = 0;
c = 0xc0;
- } else
- mb_utf8 = FALSE; /* don't draw as UTF-8 */
- if (!attr_pri) {
- saved_attr3 = char_attr; /* save current attr */
- char_attr = hl_attr(HLF_AT); /* later copied to char_attr */
- n_attr3 = 1;
+ } else {
+ mb_utf8 = false; // don't draw as UTF-8
}
+ saved_attr3 = char_attr; // save current attr
+ char_attr = hl_attr(HLF_AT); // later copied to char_attr
+ n_attr3 = 1;
}
/*
@@ -6740,8 +6767,8 @@ int showmode(void)
if (Recording
&& edit_submode == NULL /* otherwise it gets too long */
) {
- MSG_PUTS_ATTR(_("recording"), attr);
- need_clear = TRUE;
+ recording_mode(attr);
+ need_clear = true;
}
mode_displayed = TRUE;
@@ -6780,26 +6807,33 @@ static void msg_pos_mode(void)
msg_row = Rows - 1;
}
-/*
- * Delete mode message. Used when ESC is typed which is expected to end
- * Insert mode (but Insert mode didn't end yet!).
- * Caller should check "mode_displayed".
- */
-void unshowmode(int force)
+/// Delete mode message. Used when ESC is typed which is expected to end
+/// Insert mode (but Insert mode didn't end yet!).
+/// Caller should check "mode_displayed".
+void unshowmode(bool force)
{
- /*
- * Don't delete it right now, when not redrawing or inside a mapping.
- */
- if (!redrawing() || (!force && char_avail() && !KeyTyped))
- redraw_cmdline = TRUE; /* delete mode later */
- else {
+ // Don't delete it right now, when not redrawing or inside a mapping.
+ if (!redrawing() || (!force && char_avail() && !KeyTyped)) {
+ redraw_cmdline = true; // delete mode later
+ } else {
msg_pos_mode();
- if (Recording)
- MSG_PUTS_ATTR(_("recording"), hl_attr(HLF_CM));
+ if (Recording) {
+ recording_mode(hl_attr(HLF_CM));
+ }
msg_clr_eos();
}
}
+static void recording_mode(int attr)
+{
+ MSG_PUTS_ATTR(_("recording"), attr);
+ if (!shortmess(SHM_RECORDING)) {
+ char_u s[4];
+ vim_snprintf((char *)s, ARRAY_SIZE(s), " @%c", Recording);
+ MSG_PUTS_ATTR(s, attr);
+ }
+}
+
/*
* Draw the tab pages line at the top of the Vim window.
*/
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 2dd0201259..6e2b69fff7 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -453,25 +453,24 @@ void last_pat_prog(regmmatch_T *regmatch)
--emsg_off;
}
-/*
- * lowest level search function.
- * Search for 'count'th occurrence of pattern 'pat' in direction 'dir'.
- * Start at position 'pos' and return the found position in 'pos'.
- *
- * if (options & SEARCH_MSG) == 0 don't give any messages
- * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
- * if (options & SEARCH_MSG) == SEARCH_MSG give all messages
- * if (options & SEARCH_HIS) put search pattern in history
- * if (options & SEARCH_END) return position at end of match
- * if (options & SEARCH_START) accept match at pos itself
- * if (options & SEARCH_KEEP) keep previous search pattern
- * if (options & SEARCH_FOLD) match only once in a closed fold
- * if (options & SEARCH_PEEK) check for typed char, cancel search
- *
- * Return FAIL (zero) for failure, non-zero for success.
- * Returns the index of the first matching
- * subpattern plus one; one if there was none.
- */
+/// lowest level search function.
+/// Search for 'count'th occurrence of pattern 'pat' in direction 'dir'.
+/// Start at position 'pos' and return the found position in 'pos'.
+///
+/// if (options & SEARCH_MSG) == 0 don't give any messages
+/// if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
+/// if (options & SEARCH_MSG) == SEARCH_MSG give all messages
+/// if (options & SEARCH_HIS) put search pattern in history
+/// if (options & SEARCH_END) return position at end of match
+/// if (options & SEARCH_START) accept match at pos itself
+/// if (options & SEARCH_KEEP) keep previous search pattern
+/// if (options & SEARCH_FOLD) match only once in a closed fold
+/// if (options & SEARCH_PEEK) check for typed char, cancel search
+/// if (options & SEARCH_COL) start at pos->col instead of zero
+///
+/// @returns FAIL (zero) for failure, non-zero for success.
+/// the index of the first matching
+/// subpattern plus one; one if there was none.
int searchit(
win_T *win, /* window to search in, can be NULL for a
buffer without a window! */
@@ -497,6 +496,7 @@ int searchit(
pos_T start_pos;
int at_first_line;
int extra_col;
+ int start_char_len;
int match_ok;
long nmatched;
int submatch = 0;
@@ -519,16 +519,21 @@ int searchit(
// When not accepting a match at the start position set "extra_col" to a
// non-zero value. Don't do that when starting at MAXCOL, since MAXCOL + 1
// is zero.
- if ((options & SEARCH_START) || pos->col == MAXCOL) {
- extra_col = 0;
- } else if (dir != BACKWARD && has_mbyte
- && pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count
- && pos->col < MAXCOL - 2) {
+ if (pos->col == MAXCOL) {
+ start_char_len = 0;
+ } else if (has_mbyte
+ && pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count
+ && pos->col < MAXCOL - 2) {
// Watch out for the "col" being MAXCOL - 2, used in a closed fold.
- ptr = ml_get_buf(buf, pos->lnum, FALSE) + pos->col;
- extra_col = *ptr == NUL ? 1 : (*mb_ptr2len)(ptr);
+ ptr = ml_get_buf(buf, pos->lnum, false) + pos->col;
+ start_char_len = *ptr == NUL ? 1 : (*mb_ptr2len)(ptr);
} else {
- extra_col = 1;
+ start_char_len = 1;
+ }
+ if (dir == FORWARD) {
+ extra_col = (options & SEARCH_START) ? 0 : start_char_len;
+ } else {
+ extra_col = (options & SEARCH_START) ? start_char_len : 0;
}
start_pos = *pos; /* remember start pos for detecting no match */
@@ -565,16 +570,14 @@ int searchit(
if (tm != NULL && profile_passed_limit(*tm))
break;
- /*
- * Look for a match somewhere in line "lnum".
- */
+ // Look for a match somewhere in line "lnum".
+ colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0;
nmatched = vim_regexec_multi(&regmatch, win, buf,
- lnum, (colnr_T)0,
- tm
- );
- /* Abort searching on an error (e.g., out of stack). */
- if (called_emsg)
+ lnum, col, tm);
+ // Abort searching on an error (e.g., out of stack).
+ if (called_emsg) {
break;
+ }
if (nmatched > 0) {
/* match may actually be in another line when using \zs */
matchpos = regmatch.startpos[0];
@@ -635,9 +638,9 @@ int searchit(
break;
}
- if (ptr[matchcol] == NUL ||
- (nmatched = vim_regexec_multi(&regmatch, win, buf, lnum,
- matchcol, tm)) == 0) {
+ if (ptr[matchcol] == NUL
+ || (nmatched = vim_regexec_multi(&regmatch, win, buf, lnum,
+ matchcol, tm)) == 0) {
match_ok = false;
break;
}
@@ -679,16 +682,14 @@ int searchit(
|| (lnum + regmatch.endpos[0].lnum
== start_pos.lnum
&& (int)regmatch.endpos[0].col - 1
- + extra_col
- <= (int)start_pos.col))
+ < (int)start_pos.col + extra_col))
: (lnum + regmatch.startpos[0].lnum
< start_pos.lnum
|| (lnum + regmatch.startpos[0].lnum
== start_pos.lnum
&& (int)regmatch.startpos[0].col
- + extra_col
- <= (int)start_pos.col)))) {
- match_ok = TRUE;
+ < (int)start_pos.col + extra_col)))) {
+ match_ok = true;
matchpos = regmatch.startpos[0];
endpos = regmatch.endpos[0];
submatch = first_submatch(&regmatch);
@@ -877,9 +878,8 @@ static void set_vv_searchforward(void)
set_vim_var_nr(VV_SEARCHFORWARD, (long)(spats[0].off.dir == '/'));
}
-/*
- * Return the number of the first subpat that matched.
- */
+// Return the number of the first subpat that matched.
+// Return zero if none of them matched.
static int first_submatch(regmmatch_T *rp)
{
int submatch;
@@ -1027,19 +1027,18 @@ int do_search(
spats[0].off.line = FALSE;
spats[0].off.end = FALSE;
spats[0].off.off = 0;
- /*
- * Check for a line offset or a character offset.
- * For get_address (echo off) we don't check for a character
- * offset, because it is meaningless and the 's' could be a
- * substitute command.
- */
- if (*p == '+' || *p == '-' || ascii_isdigit(*p))
- spats[0].off.line = TRUE;
- else if ((options & SEARCH_OPT) &&
- (*p == 'e' || *p == 's' || *p == 'b')) {
- if (*p == 'e') /* end */
+ // Check for a line offset or a character offset.
+ // For get_address (echo off) we don't check for a character
+ // offset, because it is meaningless and the 's' could be a
+ // substitute command.
+ if (*p == '+' || *p == '-' || ascii_isdigit(*p)) {
+ spats[0].off.line = true;
+ } else if ((options & SEARCH_OPT)
+ && (*p == 'e' || *p == 's' || *p == 'b')) {
+ if (*p == 'e') { // end
spats[0].off.end = true;
- ++p;
+ }
+ p++;
}
if (ascii_isdigit(*p) || *p == '+' || *p == '-') { /* got an offset */
/* 'nr' or '+nr' or '-nr' */
@@ -1647,8 +1646,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
/*
* Look for matching #if, #else, #elif, or #endif
*/
- if (oap != NULL)
- oap->motion_type = MLINE; /* Linewise for this case only */
+ if (oap != NULL) {
+ oap->motion_type = kMTLineWise; // Linewise for this case only
+ }
if (initc != '#') {
ptr = skipwhite(skipwhite(linep) + 1);
if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0)
@@ -1783,14 +1783,13 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
}
}
- /*
- * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
- */
- if (pos.col == 0 && (flags & FM_BLOCKSTOP) &&
- (linep[0] == '{' || linep[0] == '}')) {
- if (linep[0] == findc && count == 0) /* match! */
+ // If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
+ if (pos.col == 0 && (flags & FM_BLOCKSTOP)
+ && (linep[0] == '{' || linep[0] == '}')) {
+ if (linep[0] == findc && count == 0) { // match!
return &pos;
- break; /* out of scope */
+ }
+ break; // out of scope
}
if (comment_dir) {
@@ -1959,15 +1958,15 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
if (linep[pos.col - 2] == '\'') {
pos.col -= 2;
break;
- } else if (linep[pos.col - 2] == '\\' &&
- pos.col > 2 && linep[pos.col - 3] == '\'') {
+ } else if (linep[pos.col - 2] == '\\'
+ && pos.col > 2 && linep[pos.col - 3] == '\'') {
pos.col -= 3;
break;
}
}
- } else if (linep[pos.col + 1]) { /* forward search */
- if (linep[pos.col + 1] == '\\' &&
- linep[pos.col + 2] && linep[pos.col + 3] == '\'') {
+ } else if (linep[pos.col + 1]) { // forward search
+ if (linep[pos.col + 1] == '\\'
+ && linep[pos.col + 2] && linep[pos.col + 3] == '\'') {
pos.col += 3;
break;
} else if (linep[pos.col + 2] == '\'') {
@@ -2184,30 +2183,32 @@ int findsent(int dir, long count)
* if on an empty line, skip up to a non-empty line
*/
if (gchar_pos(&pos) == NUL) {
- do
- if ((*func)(&pos) == -1)
+ do {
+ if ((*func)(&pos) == -1) {
break;
- while (gchar_pos(&pos) == NUL);
- if (dir == FORWARD)
+ }
+ } while (gchar_pos(&pos) == NUL);
+ if (dir == FORWARD) {
goto found;
- }
- /*
- * if on the start of a paragraph or a section and searching forward,
- * go to the next line
- */
- else if (dir == FORWARD && pos.col == 0 &&
- startPS(pos.lnum, NUL, FALSE)) {
- if (pos.lnum == curbuf->b_ml.ml_line_count)
+ }
+ // if on the start of a paragraph or a section and searching forward,
+ // go to the next line
+ } else if (dir == FORWARD && pos.col == 0
+ && startPS(pos.lnum, NUL, false)) {
+ if (pos.lnum == curbuf->b_ml.ml_line_count) {
return FAIL;
- ++pos.lnum;
+ }
+ pos.lnum++;
goto found;
- } else if (dir == BACKWARD)
+ } else if (dir == BACKWARD) {
decl(&pos);
+ }
- /* go back to the previous non-blank char */
- found_dot = FALSE;
- while ((c = gchar_pos(&pos)) == ' ' || c == '\t' ||
- (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL)) {
+ // go back to the previous non-blank char
+ found_dot = false;
+ while ((c = gchar_pos(&pos)) == ' ' || c == '\t'
+ || (dir == BACKWARD
+ && vim_strchr((char_u *)".!?)]\"'", c) != NULL)) {
if (vim_strchr((char_u *)".!?", c) != NULL) {
/* Only skip over a '.', '!' and '?' once. */
if (found_dot)
@@ -2371,12 +2372,14 @@ int startPS(linenr_T lnum, int para, int both)
char_u *s;
s = ml_get(lnum);
- if (*s == para || *s == '\f' || (both && *s == '}'))
- return TRUE;
- if (*s == '.' && (inmacro(p_sections, s + 1) ||
- (!para && inmacro(p_para, s + 1))))
- return TRUE;
- return FALSE;
+ if (*s == para || *s == '\f' || (both && *s == '}')) {
+ return true;
+ }
+ if (*s == '.' && (inmacro(p_sections, s + 1)
+ || (!para && inmacro(p_para, s + 1)))) {
+ return true;
+ }
+ return false;
}
/*
@@ -2793,7 +2796,7 @@ current_word (
redraw_curbuf_later(INVERTED); /* update the inversion */
} else {
oap->start = start_pos;
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
}
--count;
}
@@ -3028,7 +3031,7 @@ extend:
else
oap->inclusive = false;
oap->start = start_pos;
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
}
return OK;
}
@@ -3140,17 +3143,19 @@ current_block (
}
if (VIsual_active) {
- if (*p_sel == 'e')
- ++curwin->w_cursor.col;
- if (sol && gchar_cursor() != NUL)
- inc(&curwin->w_cursor); /* include the line break */
+ if (*p_sel == 'e') {
+ inc(&curwin->w_cursor);
+ }
+ if (sol && gchar_cursor() != NUL) {
+ inc(&curwin->w_cursor); // include the line break
+ }
VIsual = start_pos;
VIsual_mode = 'v';
redraw_curbuf_later(INVERTED); /* update the inversion */
showmode();
} else {
oap->start = start_pos;
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
oap->inclusive = false;
if (sol)
incl(&curwin->w_cursor);
@@ -3397,7 +3402,7 @@ again:
showmode();
} else {
oap->start = start_pos;
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
if (lt(end_pos, start_pos)) {
/* End is before the start: there is no text between tags; operate
* on an empty area. */
@@ -3562,7 +3567,7 @@ extend:
} else {
oap->start.lnum = start_lnum;
oap->start.col = 0;
- oap->motion_type = MLINE;
+ oap->motion_type = kMTLineWise;
}
curwin->w_cursor.lnum = end_lnum;
curwin->w_cursor.col = 0;
@@ -3804,7 +3809,7 @@ current_quote (
}
} else {
oap->start = curwin->w_cursor;
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
}
/* Set end position. */
@@ -4148,19 +4153,20 @@ find_pattern_in_path (
FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname, NULL);
already_searched = FALSE;
if (new_fname != NULL) {
- /* Check whether we have already searched in this file */
+ // Check whether we have already searched in this file
for (i = 0;; i++) {
- if (i == depth + 1)
+ if (i == depth + 1) {
i = old_files;
- if (i == max_path_depth)
+ }
+ if (i == max_path_depth) {
break;
- if (path_full_compare(new_fname, files[i].name, TRUE) & kEqualFiles) {
- if (type != CHECK_PATH &&
- action == ACTION_SHOW_ALL && files[i].matched) {
- msg_putchar('\n'); /* cursor below last one */
- if (!got_int) { /* don't display if 'q'
- typed at "--more--"
- message */
+ }
+ if (path_full_compare(new_fname, files[i].name, true) & kEqualFiles) {
+ if (type != CHECK_PATH
+ && action == ACTION_SHOW_ALL && files[i].matched) {
+ msg_putchar('\n'); // cursor below last one */
+ if (!got_int) { // don't display if 'q' typed at "--more--"
+ // message
msg_home_replace_hl(new_fname);
MSG_PUTS(_(" (includes previously listed match)"));
prev_fname = NULL;
@@ -4175,15 +4181,15 @@ find_pattern_in_path (
}
if (type == CHECK_PATH && (action == ACTION_SHOW_ALL
- || (new_fname == NULL &&
- !already_searched))) {
- if (did_show)
- msg_putchar('\n'); /* cursor below last one */
- else {
- gotocmdline(TRUE); /* cursor at status line */
+ || (new_fname == NULL && !already_searched))) {
+ if (did_show) {
+ msg_putchar('\n'); // cursor below last one
+ } else {
+ gotocmdline(true); // cursor at status line
MSG_PUTS_TITLE(_("--- Included files "));
- if (action != ACTION_SHOW_ALL)
+ if (action != ACTION_SHOW_ALL) {
MSG_PUTS_TITLE(_("not found "));
+ }
MSG_PUTS_TITLE(_("in path ---\n"));
}
did_show = TRUE;
@@ -4342,16 +4348,15 @@ search_line:
&& vim_regexec(&regmatch, line, (colnr_T)(p - line))) {
matched = TRUE;
startp = regmatch.startp[0];
- /*
- * Check if the line is not a comment line (unless we are
- * looking for a define). A line starting with "# define"
- * is not considered to be a comment line.
- */
+ // Check if the line is not a comment line (unless we are
+ // looking for a define). A line starting with "# define"
+ // is not considered to be a comment line.
if (skip_comments) {
- if ((*line != '#' ||
- STRNCMP(skipwhite(line + 1), "define", 6) != 0)
- && get_leader_len(line, NULL, FALSE, TRUE))
- matched = FALSE;
+ if ((*line != '#'
+ || STRNCMP(skipwhite(line + 1), "define", 6) != 0)
+ && get_leader_len(line, NULL, false, true)) {
+ matched = false;
+ }
/*
* Also check for a "/ *" or "/ /" before the match.
diff --git a/src/nvim/search.h b/src/nvim/search.h
index 6947f79d49..d4e40cb287 100644
--- a/src/nvim/search.h
+++ b/src/nvim/search.h
@@ -15,19 +15,20 @@
#define ACTION_SHOW_ALL 4
#define ACTION_EXPAND 5
-/* Values for 'options' argument in do_search() and searchit() */
-#define SEARCH_REV 0x01 /* go in reverse of previous dir. */
-#define SEARCH_ECHO 0x02 /* echo the search command and handle options */
-#define SEARCH_MSG 0x0c /* give messages (yes, it's not 0x04) */
-#define SEARCH_NFMSG 0x08 /* give all messages except not found */
-#define SEARCH_OPT 0x10 /* interpret optional flags */
-#define SEARCH_HIS 0x20 /* put search pattern in history */
-#define SEARCH_END 0x40 /* put cursor at end of match */
-#define SEARCH_NOOF 0x80 /* don't add offset to position */
-#define SEARCH_START 0x100 /* start search without col offset */
-#define SEARCH_MARK 0x200 /* set previous context mark */
-#define SEARCH_KEEP 0x400 /* keep previous search pattern */
-#define SEARCH_PEEK 0x800 /* peek for typed char, cancel search */
+// Values for 'options' argument in do_search() and searchit()
+#define SEARCH_REV 0x01 ///< go in reverse of previous dir.
+#define SEARCH_ECHO 0x02 ///< echo the search command and handle options
+#define SEARCH_MSG 0x0c ///< give messages (yes, it's not 0x04)
+#define SEARCH_NFMSG 0x08 ///< give all messages except not found
+#define SEARCH_OPT 0x10 ///< interpret optional flags
+#define SEARCH_HIS 0x20 ///< put search pattern in history
+#define SEARCH_END 0x40 ///< put cursor at end of match
+#define SEARCH_NOOF 0x80 ///< don't add offset to position
+#define SEARCH_START 0x100 ///< start search without col offset
+#define SEARCH_MARK 0x200 ///< set previous context mark
+#define SEARCH_KEEP 0x400 ///< keep previous search pattern
+#define SEARCH_PEEK 0x800 ///< peek for typed char, cancel search
+#define SEARCH_COL 0x1000 ///< start at specified column instead of zero
/* Values for flags argument for findmatchlimit() */
#define FM_BACKWARD 0x01 /* search backwards */
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index def2de9b1a..51c8597d53 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -31,7 +31,6 @@
#include "nvim/misc2.h"
#include "nvim/ex_getln.h"
#include "nvim/search.h"
-#include "nvim/eval.h"
#include "nvim/regexp.h"
#include "nvim/eval_defs.h"
#include "nvim/version.h"
@@ -39,13 +38,15 @@
#include "nvim/fileio.h"
#include "nvim/strings.h"
#include "nvim/quickfix.h"
+#include "nvim/eval/encode.h"
+#include "nvim/eval/decode.h"
#include "nvim/lib/khash.h"
#include "nvim/lib/kvec.h"
#ifdef HAVE_BE64TOH
# define _BSD_SOURCE 1
# define _DEFAULT_SOURCE 1
-# include <endian.h>
+# include ENDIAN_INCLUDE_FILE
#endif
// Note: when using bufset hash pointers are intentionally casted to uintptr_t
@@ -65,9 +66,6 @@ KHASH_SET_INIT_STR(strset)
((char *) copy_option_part((char_u **) src, (char_u *) dest, __VA_ARGS__))
#define find_shada_parameter(...) \
((const char *) find_shada_parameter(__VA_ARGS__))
-#define emsg2(a, b) emsg2((char_u *) a, (char_u *) b)
-#define emsg3(a, b, c) emsg3((char_u *) a, (char_u *) b, (char_u *) c)
-#define emsgu(a, ...) emsgu((char_u *) a, __VA_ARGS__)
#define home_replace_save(a, b) \
((char *)home_replace_save(a, (char_u *)b))
#define home_replace(a, b, c, d, e) \
@@ -285,7 +283,7 @@ typedef struct {
} history_item;
struct reg {
char name;
- uint8_t type;
+ MotionType type;
char **contents;
size_t contents_size;
size_t width;
@@ -477,7 +475,7 @@ static const ShadaEntry sd_default_values[] = {
.additional_elements = NULL),
DEF_SDE(Register, reg,
.name = NUL,
- .type = MCHAR,
+ .type = kMTCharWise,
.contents = NULL,
.contents_size = 0,
.width = 0,
@@ -761,7 +759,7 @@ static void close_sd_writer(ShaDaWriteDef *const sd_writer)
{
const int fd = (int)(intptr_t) sd_writer->cookie;
if (os_fsync(fd) < 0) {
- emsg2(_(SERR "System error while synchronizing ShaDa file: %s"),
+ emsgf(_(SERR "System error while synchronizing ShaDa file: %s"),
os_strerror(errno));
errno = 0;
}
@@ -811,11 +809,11 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader,
{
if (sd_reader->skip(sd_reader, offset) != OK) {
if (sd_reader->error != NULL) {
- emsg2(_(SERR "System error while skipping in ShaDa file: %s"),
+ emsgf(_(SERR "System error while skipping in ShaDa file: %s"),
sd_reader->error);
return kSDReadStatusReadError;
} else if (sd_reader->eof) {
- emsgu(_(RCERR "Error while reading ShaDa file: "
+ emsgf(_(RCERR "Error while reading ShaDa file: "
"last entry specified that it occupies %" PRIu64 " bytes, "
"but file ended earlier"),
(uint64_t) offset);
@@ -849,7 +847,7 @@ open_file_start:
goto open_file_start;
}
if (fd != UV_EEXIST) {
- emsg3(_(SERR "System error while opening ShaDa file %s: %s"),
+ emsgf(_(SERR "System error while opening ShaDa file %s: %s"),
fname, os_strerror(fd));
}
return fd;
@@ -897,7 +895,7 @@ close_file_start:
errno = 0;
goto close_file_start;
} else {
- emsg2(_(SERR "System error while closing ShaDa file: %s"),
+ emsgf(_(SERR "System error while closing ShaDa file: %s"),
strerror(errno));
errno = 0;
}
@@ -934,7 +932,7 @@ static int msgpack_sd_writer_write(void *data, const char *buf, size_t len)
ShaDaWriteDef *const sd_writer = (ShaDaWriteDef *) data;
ptrdiff_t written_bytes = sd_writer->write(sd_writer, buf, len);
if (written_bytes == -1) {
- emsg2(_(SERR "System error while writing ShaDa file: %s"),
+ emsgf(_(SERR "System error while writing ShaDa file: %s"),
sd_writer->error);
return -1;
}
@@ -981,7 +979,7 @@ static int shada_read_file(const char *const file, const int flags)
if (of_ret != 0) {
if (of_ret == UV_ENOENT && (flags & kShaDaMissingError)) {
- emsg3(_(SERR "System error while opening ShaDa file %s for reading: %s"),
+ emsgf(_(SERR "System error while opening ShaDa file %s for reading: %s"),
fname, os_strerror(of_ret));
}
xfree(fname);
@@ -1409,9 +1407,9 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
break;
}
case kSDItemRegister: {
- if (cur_entry.data.reg.type != MCHAR
- && cur_entry.data.reg.type != MLINE
- && cur_entry.data.reg.type != MBLOCK) {
+ if (cur_entry.data.reg.type != kMTCharWise
+ && cur_entry.data.reg.type != kMTLineWise
+ && cur_entry.data.reg.type != kMTBlockWise) {
shada_free_shada_entry(&cur_entry);
break;
}
@@ -1687,8 +1685,9 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
do { \
if ((src) != NULL) { \
for (listitem_T *li = (src)->lv_first; li != NULL; li = li->li_next) { \
- if (vim_to_msgpack(spacker, &li->li_tv, \
- _("additional elements of ShaDa " what)) == FAIL) { \
+ if (encode_vim_to_msgpack(spacker, &li->li_tv, \
+ _("additional elements of ShaDa " what)) \
+ == FAIL) { \
goto shada_pack_entry_error; \
} \
} \
@@ -1706,8 +1705,9 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
const size_t key_len = strlen((const char *) hi->hi_key); \
msgpack_pack_str(spacker, key_len); \
msgpack_pack_str_body(spacker, (const char *) hi->hi_key, key_len); \
- if (vim_to_msgpack(spacker, &di->di_tv, \
- _("additional data of ShaDa " what)) == FAIL) { \
+ if (encode_vim_to_msgpack(spacker, &di->di_tv, \
+ _("additional data of ShaDa " what)) \
+ == FAIL) { \
goto shada_pack_entry_error; \
} \
} \
@@ -1757,7 +1757,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
char vardesc[256] = "variable g:";
memcpy(&vardesc[sizeof("variable g:") - 1], varname.data,
varname.size + 1);
- if (vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc)
+ if (encode_vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc)
== FAIL) {
ret = kSDWriteIgnError;
EMSG2(_(WERR "Failed to write variable %s"),
@@ -1882,7 +1882,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
msgpack_pack_char(spacker, entry.data.reg.name);
if (!CHECK_DEFAULT(entry, reg.type)) {
PACK_STATIC_STR(REG_KEY_TYPE);
- msgpack_pack_uint8(spacker, entry.data.reg.type);
+ msgpack_pack_uint8(spacker, (uint8_t)entry.data.reg.type);
}
if (!CHECK_DEFAULT(entry, reg.width)) {
PACK_STATIC_STR(REG_KEY_WIDTH);
@@ -2159,7 +2159,7 @@ shada_parse_msgpack_read_next: {}
break;
}
case MSGPACK_UNPACK_PARSE_ERROR: {
- emsgu(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error "
+ emsgf(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error "
"at position %" PRIu64),
(uint64_t) initial_fpos);
ret = kSDReadStatusNotShaDa;
@@ -2176,7 +2176,7 @@ shada_parse_msgpack_read_next: {}
break;
}
case MSGPACK_UNPACK_CONTINUE: {
- emsgu(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string "
+ emsgf(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string "
"at position %" PRIu64),
(uint64_t) initial_fpos);
ret = kSDReadStatusNotShaDa;
@@ -2184,7 +2184,7 @@ shada_parse_msgpack_read_next: {}
}
case MSGPACK_UNPACK_EXTRA_BYTES: {
shada_parse_msgpack_extra_bytes:
- emsgu(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string "
+ emsgf(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string "
"at position %" PRIu64),
(uint64_t) initial_fpos);
ret = kSDReadStatusNotShaDa;
@@ -2757,8 +2757,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
.reg = {
.contents = (char **) reg.y_array,
.contents_size = (size_t) reg.y_size,
- .type = (uint8_t) reg.y_type,
- .width = (size_t) (reg.y_type == MBLOCK ? reg.y_width : 0),
+ .type = reg.y_type,
+ .width = (size_t) (reg.y_type == kMTBlockWise ? reg.y_width : 0),
.additional_data = reg.additional_data,
.name = name,
}
@@ -3265,11 +3265,11 @@ static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader,
(void) read_bytes;
if (sd_reader->error != NULL) {
- emsg2(_(SERR "System error while reading ShaDa file: %s"),
+ emsgf(_(SERR "System error while reading ShaDa file: %s"),
sd_reader->error);
return kSDReadStatusReadError;
} else if (sd_reader->eof) {
- emsgu(_(RCERR "Error while reading ShaDa file: "
+ emsgf(_(RCERR "Error while reading ShaDa file: "
"last entry specified that it occupies %" PRIu64 " bytes, "
"but file ended earlier"),
(uint64_t) length);
@@ -3304,11 +3304,11 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader,
if (first_char == EOF) {
if (sd_reader->error) {
- emsg2(_(SERR "System error while reading integer from ShaDa file: %s"),
+ emsgf(_(SERR "System error while reading integer from ShaDa file: %s"),
sd_reader->error);
return kSDReadStatusReadError;
} else if (sd_reader->eof) {
- emsgu(_(RCERR "Error while reading ShaDa file: "
+ emsgf(_(RCERR "Error while reading ShaDa file: "
"expected positive integer at position %" PRIu64
", but got nothing"),
(uint64_t) fpos);
@@ -3339,7 +3339,7 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader,
break;
}
default: {
- emsgu(_(RCERR "Error while reading ShaDa file: "
+ emsgf(_(RCERR "Error while reading ShaDa file: "
"expected positive integer at position %" PRIu64),
(uint64_t) fpos);
return kSDReadStatusNotShaDa;
@@ -3403,18 +3403,18 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv,
proc) \
do { \
if (!(condition)) { \
- emsgu(_(READERR(entry_name, error_desc)), initial_fpos); \
+ emsgf(_(READERR(entry_name, error_desc)), initial_fpos); \
CLEAR_GA_AND_ERROR_OUT(ad_ga); \
} \
tgt = proc(obj.via.attr); \
} while (0)
#define CHECK_KEY_IS_STR(entry_name) \
if (unpacked.data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) { \
- emsgu(_(READERR(entry_name, "has key which is not a string")), \
+ emsgf(_(READERR(entry_name, "has key which is not a string")), \
initial_fpos); \
CLEAR_GA_AND_ERROR_OUT(ad_ga); \
} else if (unpacked.data.via.map.ptr[i].key.via.str.size == 0) { \
- emsgu(_(READERR(entry_name, "has empty key")), initial_fpos); \
+ emsgf(_(READERR(entry_name, "has empty key")), initial_fpos); \
CLEAR_GA_AND_ERROR_OUT(ad_ga); \
}
#define CHECKED_KEY(entry_name, name, error_desc, tgt, condition, attr, proc) \
@@ -3477,7 +3477,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv,
typval_T adtv; \
if (msgpack_to_vim(obj, &adtv) == FAIL \
|| adtv.v_type != VAR_DICT) { \
- emsgu(_(READERR(name, \
+ emsgf(_(READERR(name, \
"cannot be converted to a VimL dictionary")), \
initial_fpos); \
ga_clear(&ad_ga); \
@@ -3502,7 +3502,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv,
}; \
typval_T aetv; \
if (msgpack_to_vim(obj, &aetv) == FAIL) { \
- emsgu(_(READERR(name, "cannot be converted to a VimL list")), \
+ emsgf(_(READERR(name, "cannot be converted to a VimL list")), \
initial_fpos); \
clear_tv(&aetv); \
goto shada_read_next_item_error; \
@@ -3570,7 +3570,7 @@ shada_read_next_item_start:
// kSDItemUnknown cannot possibly pass that far because it is -1 and that
// will fail in msgpack_read_uint64. But kSDItemMissing may and it will
// otherwise be skipped because (1 << 0) will never appear in flags.
- emsgu(_(RCERR "Error while reading ShaDa file: "
+ emsgf(_(RCERR "Error while reading ShaDa file: "
"there is an item at position %" PRIu64 " "
"that must not be there: Missing items are "
"for internal uses only"),
@@ -3640,14 +3640,14 @@ shada_read_next_item_start:
switch ((ShadaEntryType) type_u64) {
case kSDItemHeader: {
if (!msgpack_rpc_to_dictionary(&(unpacked.data), &(entry->data.header))) {
- emsgu(_(READERR("header", "is not a dictionary")), initial_fpos);
+ emsgf(_(READERR("header", "is not a dictionary")), initial_fpos);
goto shada_read_next_item_error;
}
break;
}
case kSDItemSearchPattern: {
if (unpacked.data.type != MSGPACK_OBJECT_MAP) {
- emsgu(_(READERR("search pattern", "is not a dictionary")),
+ emsgf(_(READERR("search pattern", "is not a dictionary")),
initial_fpos);
goto shada_read_next_item_error;
}
@@ -3678,7 +3678,7 @@ shada_read_next_item_start:
ADDITIONAL_KEY
}
if (entry->data.search_pattern.pat == NULL) {
- emsgu(_(READERR("search pattern", "has no pattern")), initial_fpos);
+ emsgf(_(READERR("search pattern", "has no pattern")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
SET_ADDITIONAL_DATA(entry->data.search_pattern.additional_data,
@@ -3690,7 +3690,7 @@ shada_read_next_item_start:
case kSDItemGlobalMark:
case kSDItemLocalMark: {
if (unpacked.data.type != MSGPACK_OBJECT_MAP) {
- emsgu(_(READERR("mark", "is not a dictionary")), initial_fpos);
+ emsgf(_(READERR("mark", "is not a dictionary")), initial_fpos);
goto shada_read_next_item_error;
}
garray_T ad_ga;
@@ -3699,7 +3699,7 @@ shada_read_next_item_start:
CHECK_KEY_IS_STR("mark")
if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, KEY_NAME_CHAR)) {
if (type_u64 == kSDItemJump || type_u64 == kSDItemChange) {
- emsgu(_(READERR("mark", "has n key which is only valid for "
+ emsgf(_(READERR("mark", "has n key which is only valid for "
"local and global mark entries")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
@@ -3716,15 +3716,15 @@ shada_read_next_item_start:
ADDITIONAL_KEY
}
if (entry->data.filemark.fname == NULL) {
- emsgu(_(READERR("mark", "is missing file name")), initial_fpos);
+ emsgf(_(READERR("mark", "is missing file name")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
if (entry->data.filemark.mark.lnum <= 0) {
- emsgu(_(READERR("mark", "has invalid line number")), initial_fpos);
+ emsgf(_(READERR("mark", "has invalid line number")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
if (entry->data.filemark.mark.col < 0) {
- emsgu(_(READERR("mark", "has invalid column number")), initial_fpos);
+ emsgf(_(READERR("mark", "has invalid column number")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
SET_ADDITIONAL_DATA(entry->data.filemark.additional_data, "mark");
@@ -3732,7 +3732,7 @@ shada_read_next_item_start:
}
case kSDItemRegister: {
if (unpacked.data.type != MSGPACK_OBJECT_MAP) {
- emsgu(_(READERR("register", "is not a dictionary")), initial_fpos);
+ emsgf(_(READERR("register", "is not a dictionary")), initial_fpos);
goto shada_read_next_item_error;
}
garray_T ad_ga;
@@ -3742,14 +3742,14 @@ shada_read_next_item_start:
if (CHECK_KEY(unpacked.data.via.map.ptr[i].key,
REG_KEY_CONTENTS)) {
if (unpacked.data.via.map.ptr[i].val.type != MSGPACK_OBJECT_ARRAY) {
- emsgu(_(READERR("register",
+ emsgf(_(READERR("register",
"has " REG_KEY_CONTENTS
" key with non-array value")),
initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
if (unpacked.data.via.map.ptr[i].val.via.array.size == 0) {
- emsgu(_(READERR("register",
+ emsgf(_(READERR("register",
"has " REG_KEY_CONTENTS " key with empty array")),
initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
@@ -3758,7 +3758,7 @@ shada_read_next_item_start:
unpacked.data.via.map.ptr[i].val.via.array;
for (size_t i = 0; i < arr.size; i++) {
if (arr.ptr[i].type != MSGPACK_OBJECT_BIN) {
- emsgu(_(READERR("register", "has " REG_KEY_CONTENTS " array "
+ emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " array "
"with non-binary value")), initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
@@ -3778,7 +3778,7 @@ shada_read_next_item_start:
ADDITIONAL_KEY
}
if (entry->data.reg.contents == NULL) {
- emsgu(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")),
+ emsgf(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")),
initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
@@ -3787,29 +3787,29 @@ shada_read_next_item_start:
}
case kSDItemHistoryEntry: {
if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) {
- emsgu(_(READERR("history", "is not an array")), initial_fpos);
+ emsgf(_(READERR("history", "is not an array")), initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.size < 2) {
- emsgu(_(READERR("history", "does not have enough elements")),
+ emsgf(_(READERR("history", "does not have enough elements")),
initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.ptr[0].type
!= MSGPACK_OBJECT_POSITIVE_INTEGER) {
- emsgu(_(READERR("history", "has wrong history type type")),
+ emsgf(_(READERR("history", "has wrong history type type")),
initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.ptr[1].type
!= MSGPACK_OBJECT_BIN) {
- emsgu(_(READERR("history", "has wrong history string type")),
+ emsgf(_(READERR("history", "has wrong history string type")),
initial_fpos);
goto shada_read_next_item_error;
}
if (memchr(unpacked.data.via.array.ptr[1].via.bin.ptr, 0,
unpacked.data.via.array.ptr[1].via.bin.size) != NULL) {
- emsgu(_(READERR("history", "contains string with zero byte inside")),
+ emsgf(_(READERR("history", "contains string with zero byte inside")),
initial_fpos);
goto shada_read_next_item_error;
}
@@ -3819,13 +3819,13 @@ shada_read_next_item_start:
entry->data.history_item.histtype == HIST_SEARCH;
if (is_hist_search) {
if (unpacked.data.via.array.size < 3) {
- emsgu(_(READERR("search history",
+ emsgf(_(READERR("search history",
"does not have separator character")), initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.ptr[2].type
!= MSGPACK_OBJECT_POSITIVE_INTEGER) {
- emsgu(_(READERR("search history",
+ emsgf(_(READERR("search history",
"has wrong history separator type")), initial_fpos);
goto shada_read_next_item_error;
}
@@ -3867,22 +3867,16 @@ shada_read_next_item_hist_no_conv:
}
case kSDItemVariable: {
if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) {
- emsgu(_(READERR("variable", "is not an array")), initial_fpos);
+ emsgf(_(READERR("variable", "is not an array")), initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.size < 2) {
- emsgu(_(READERR("variable", "does not have enough elements")),
+ emsgf(_(READERR("variable", "does not have enough elements")),
initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) {
- emsgu(_(READERR("variable", "has wrong variable name type")),
- initial_fpos);
- goto shada_read_next_item_error;
- }
- if (unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_NIL
- || unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_EXT) {
- emsgu(_(READERR("variable", "has wrong variable value type")),
+ emsgf(_(READERR("variable", "has wrong variable name type")),
initial_fpos);
goto shada_read_next_item_error;
}
@@ -3891,7 +3885,7 @@ shada_read_next_item_hist_no_conv:
unpacked.data.via.array.ptr[0].via.bin.size);
if (msgpack_to_vim(unpacked.data.via.array.ptr[1],
&(entry->data.global_var.value)) == FAIL) {
- emsgu(_(READERR("variable", "has value that cannot "
+ emsgf(_(READERR("variable", "has value that cannot "
"be converted to the VimL value")), initial_fpos);
goto shada_read_next_item_error;
}
@@ -3912,16 +3906,16 @@ shada_read_next_item_hist_no_conv:
}
case kSDItemSubString: {
if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) {
- emsgu(_(READERR("sub string", "is not an array")), initial_fpos);
+ emsgf(_(READERR("sub string", "is not an array")), initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.size < 1) {
- emsgu(_(READERR("sub string", "does not have enough elements")),
+ emsgf(_(READERR("sub string", "does not have enough elements")),
initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) {
- emsgu(_(READERR("sub string", "has wrong sub string type")),
+ emsgf(_(READERR("sub string", "has wrong sub string type")),
initial_fpos);
goto shada_read_next_item_error;
}
@@ -3934,7 +3928,7 @@ shada_read_next_item_hist_no_conv:
}
case kSDItemBufferList: {
if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) {
- emsgu(_(READERR("buffer list", "is not an array")), initial_fpos);
+ emsgf(_(READERR("buffer list", "is not an array")), initial_fpos);
goto shada_read_next_item_error;
}
if (unpacked.data.via.array.size == 0) {
@@ -3951,7 +3945,7 @@ shada_read_next_item_hist_no_conv:
{
msgpack_unpacked unpacked = unpacked_2;
if (unpacked.data.type != MSGPACK_OBJECT_MAP) {
- emsgu(_(RERR "Error while reading ShaDa file: "
+ emsgf(_(RERR "Error while reading ShaDa file: "
"buffer list at position %" PRIu64 " "
"contains entry that is not a dictionary"),
initial_fpos);
@@ -3976,21 +3970,21 @@ shada_read_next_item_hist_no_conv:
}
}
if (entry->data.buffer_list.buffers[i].pos.lnum <= 0) {
- emsgu(_(RERR "Error while reading ShaDa file: "
+ emsgf(_(RERR "Error while reading ShaDa file: "
"buffer list at position %" PRIu64 " "
"contains entry with invalid line number"),
initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
if (entry->data.buffer_list.buffers[i].pos.col < 0) {
- emsgu(_(RERR "Error while reading ShaDa file: "
+ emsgf(_(RERR "Error while reading ShaDa file: "
"buffer list at position %" PRIu64 " "
"contains entry with invalid column number"),
initial_fpos);
CLEAR_GA_AND_ERROR_OUT(ad_ga);
}
if (entry->data.buffer_list.buffers[i].fname == NULL) {
- emsgu(_(RERR "Error while reading ShaDa file: "
+ emsgf(_(RERR "Error while reading ShaDa file: "
"buffer list at position %" PRIu64 " "
"contains entry that does not have a file name"),
initial_fpos);
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index cc7dc6210c..84906a3548 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -45,6 +45,9 @@
// Use SPELL_PRINTTREE for debugging: dump the word tree after adding a word.
// Only use it for small word lists!
+// Use SPELL_COMPRESS_ALLWAYS for debugging: compress the word tree after
+// adding a word. Only use it for small word lists!
+
// Use DEBUG_TRIEWALK to print the changes made in suggest_trie_walk() for a
// specific word.
@@ -156,6 +159,8 @@
//
// sectionID == SN_NOSPLITSUGS: nothing
//
+// sectionID == SN_NOCOMPOUNDSUGS: nothing
+//
// sectionID == SN_WORDS: <word> ...
// <word> N bytes NUL terminated common word
//
@@ -319,7 +324,6 @@
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
-#include "nvim/tempfile.h"
#include "nvim/undo.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@@ -483,7 +487,7 @@ struct slang_S {
regprog_T **sl_prefprog; // table with regprogs for prefixes
garray_T sl_rep; // list of fromto_T entries from REP lines
- short sl_rep_first[256]; // indexes where byte first appears, -1 if
+ int16_t sl_rep_first[256]; // indexes where byte first appears, -1 if
// there is none
garray_T sl_sal; // list of salitem_T entries from SAL lines
salfirst_T sl_sal_first[256]; // indexes where byte first appears, -1 if
@@ -495,8 +499,9 @@ struct slang_S {
// "sl_sal_first" maps chars, when has_mbyte
// "sl_sal" is a list of wide char lists.
garray_T sl_repsal; // list of fromto_T entries from REPSAL lines
- short sl_repsal_first[256]; // sl_rep_first for REPSAL lines
- bool sl_nosplitsugs; // don't suggest splitting a word
+ int16_t sl_repsal_first[256]; // sl_rep_first for REPSAL lines
+ bool sl_nosplitsugs; // don't suggest splitting a word
+ bool sl_nocompoundsugs; // don't suggest compounding
// Info from the .sug file. Loaded on demand.
time_t sl_sugtime; // timestamp for .sug file
@@ -559,6 +564,7 @@ typedef struct langp_S {
#define SN_WORDS 13 // common words
#define SN_NOSPLITSUGS 14 // don't split word for suggestions
#define SN_INFO 15 // info section
+#define SN_NOCOMPOUNDSUGS 16 // don't compound for suggestions
#define SN_END 255 // end of sections
#define SNF_REQUIRED 1 // <sectionflags>: required section
@@ -949,6 +955,7 @@ typedef struct spellinfo_S {
char_u *si_sofoto; // SOFOTO text
int si_nosugfile; // NOSUGFILE item found
int si_nosplitsugs; // NOSPLITSUGS item found
+ int si_nocompoundsugs; // NOCOMPOUNDSUGS item found
int si_followup; // soundsalike: ?
int si_collapse; // soundsalike: ?
hashtab_T si_commonwords; // hashtable for common words
@@ -2332,14 +2339,17 @@ static void spell_load_lang(char_u *lang)
if (r == FAIL) {
if (starting) {
- // Some startup file sets &spell, but the necessary files don't exist:
- // try to prompt the user at VimEnter. Also set spell again. #3027
- do_cmdline_cmd(
- "autocmd VimEnter * call spellfile#LoadFile(&spelllang)|set spell");
+ // Prompt the user at VimEnter if spell files are missing. #3027
+ // Plugins aren't loaded yet, so spellfile.vim cannot handle this case.
+ char autocmd_buf[128] = { 0 };
+ snprintf(autocmd_buf, sizeof(autocmd_buf),
+ "autocmd VimEnter * call spellfile#LoadFile('%s')|set spell",
+ lang);
+ do_cmdline_cmd(autocmd_buf);
} else {
smsg(
_("Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""),
- lang, spell_enc(), lang);
+ lang, spell_enc(), lang);
}
} else if (sl.sl_slang != NULL) {
// At least one file was loaded, now load ALL the additions.
@@ -2664,7 +2674,11 @@ spell_load_file (
break;
case SN_NOSPLITSUGS:
- lp->sl_nosplitsugs = true; // <timestamp>
+ lp->sl_nosplitsugs = true;
+ break;
+
+ case SN_NOCOMPOUNDSUGS:
+ lp->sl_nocompoundsugs = true;
break;
case SN_COMPOUND:
@@ -2866,7 +2880,7 @@ static int read_prefcond_section(FILE *fd, slang_T *lp)
// Read REP or REPSAL items section from "fd": <repcount> <rep> ...
// Return SP_*ERROR flags.
-static int read_rep_section(FILE *fd, garray_T *gap, short *first)
+static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first)
{
int cnt;
fromto_T *ftp;
@@ -4264,9 +4278,9 @@ static void spell_print_node(wordnode_T *node, int depth)
PRINTSOME(line1, depth, "(%d)", node->wn_nr, 0);
PRINTSOME(line2, depth, " ", 0, 0);
PRINTSOME(line3, depth, " ", 0, 0);
- msg(line1);
- msg(line2);
- msg(line3);
+ msg((char_u *)line1);
+ msg((char_u *)line2);
+ msg((char_u *)line3);
} else {
node->wn_u1.index = TRUE;
@@ -4287,9 +4301,9 @@ static void spell_print_node(wordnode_T *node, int depth)
PRINTSOME(line3, depth, " ", 0, 0);
if (node->wn_byte == NUL) {
- msg(line1);
- msg(line2);
- msg(line3);
+ msg((char_u *)line1);
+ msg((char_u *)line2);
+ msg((char_u *)line3);
}
// do the children
@@ -4631,6 +4645,8 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
spin->si_nobreak = true;
} else if (is_aff_rule(items, itemcnt, "NOSPLITSUGS", 1)) {
spin->si_nosplitsugs = true;
+ } else if (is_aff_rule(items, itemcnt, "NOCOMPOUNDSUGS", 1)) {
+ spin->si_nocompoundsugs = true;
} else if (is_aff_rule(items, itemcnt, "NOSUGFILE", 1)) {
spin->si_nosugfile = true;
} else if (is_aff_rule(items, itemcnt, "PFXPOSTPONE", 1)) {
@@ -6287,7 +6303,7 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int
node = *prev;
}
#ifdef SPELL_PRINTTREE
- smsg("Added \"%s\"", word);
+ smsg((char_u *)"Added \"%s\"", word);
spell_print_tree(root->wn_sibling);
#endif
@@ -6310,8 +6326,8 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int
// 3. When compressed before, added "compress_added" words
// (si_compress_cnt == 1) and the number of free nodes drops below the
// maximum word length.
-#ifndef SPELL_PRINTTREE
- if (spin->si_compress_cnt == 1
+#ifndef SPELL_COMPRESS_ALLWAYS
+ if (spin->si_compress_cnt == 1 // NOLINT(readability/braces)
? spin->si_free_count < MAXWLEN
: spin->si_blocks_cnt >= compress_start)
#endif
@@ -6855,6 +6871,15 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
put_bytes(fd, 0, 4); // <sectionlen>
}
+ // SN_NOCOMPUNDSUGS: nothing
+ // This is used to notify that no suggestions with compounds are to be
+ // made.
+ if (spin->si_nocompoundsugs) {
+ putc(SN_NOCOMPOUNDSUGS, fd); // <sectionID>
+ putc(0, fd); // <sectionflags>
+ put_bytes(fd, 0, 4); // <sectionlen>
+ }
+
// SN_COMPOUND: compound info.
// We don't mark it required, when not supported all compound words will
// be bad words.
@@ -9769,6 +9794,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// be possible to compound another (short) word.
try_compound = false;
if (!soundfold
+ && !slang->sl_nocompoundsugs
&& slang->sl_compprog != NULL
&& ((unsigned)flags >> 24) != 0
&& sp->ts_twordlen - sp->ts_splitoff
@@ -9789,21 +9815,21 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// For NOBREAK we never try splitting, it won't make any word
// valid.
- if (slang->sl_nobreak)
+ if (slang->sl_nobreak && !slang->sl_nocompoundsugs) {
try_compound = true;
-
- // If we could add a compound word, and it's also possible to
- // split at this point, do the split first and set
- // TSF_DIDSPLIT to avoid doing it again.
- else if (!fword_ends
- && try_compound
- && (sp->ts_flags & TSF_DIDSPLIT) == 0) {
+ } else if (!fword_ends
+ && try_compound
+ && (sp->ts_flags & TSF_DIDSPLIT) == 0) {
+ // If we could add a compound word, and it's also possible to
+ // split at this point, do the split first and set
+ // TSF_DIDSPLIT to avoid doing it again.
try_compound = false;
sp->ts_flags |= TSF_DIDSPLIT;
--sp->ts_curi; // do the same NUL again
compflags[sp->ts_complen] = NUL;
- } else
+ } else {
sp->ts_flags &= ~TSF_DIDSPLIT;
+ }
if (try_split || try_compound) {
if (!try_compound && (!fword_ends || !goodword_ends)) {
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 24422c71fb..1f9dbd8228 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -41,6 +41,8 @@
#include "nvim/os/os.h"
#include "nvim/os/time.h"
+static bool did_syntax_onoff = false;
+
// Structure that stores information about a highlight group.
// The ID of a highlight group is also called group ID. It is the index in
// the highlight_ga array PLUS ONE.
@@ -60,8 +62,10 @@ struct hl_group {
int sg_gui; // "gui=" highlighting attributes
RgbValue sg_rgb_fg; // RGB foreground color
RgbValue sg_rgb_bg; // RGB background color
+ RgbValue sg_rgb_sp; // RGB special color
uint8_t *sg_rgb_fg_name; // RGB foreground color name
uint8_t *sg_rgb_bg_name; // RGB background color name
+ uint8_t *sg_rgb_sp_name; // RGB special color name
};
#define SG_CTERM 2 // cterm has been set
@@ -2613,33 +2617,37 @@ find_endpos (
IF_SYN_TIME(&spp_skip->sp_time));
spp_skip->sp_prog = regmatch.regprog;
if (r && regmatch.startpos[0].col <= best_regmatch.startpos[0].col) {
- /* Add offset to skip pattern match */
+ // Add offset to skip pattern match
syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
- /* If the skip pattern goes on to the next line, there is no
- * match with an end pattern in this line. */
- if (pos.lnum > startpos->lnum)
+ // If the skip pattern goes on to the next line, there is no
+ // match with an end pattern in this line.
+ if (pos.lnum > startpos->lnum) {
break;
+ }
- line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
+ line = ml_get_buf(syn_buf, startpos->lnum, false);
+ int line_len = (int)STRLEN(line);
- /* take care of an empty match or negative offset */
- if (pos.col <= matchcol)
- ++matchcol;
- else if (pos.col <= regmatch.endpos[0].col)
+ // take care of an empty match or negative offset
+ if (pos.col <= matchcol) {
+ matchcol++;
+ } else if (pos.col <= regmatch.endpos[0].col) {
matchcol = pos.col;
- else
- /* Be careful not to jump over the NUL at the end-of-line */
+ } else {
+ // Be careful not to jump over the NUL at the end-of-line
for (matchcol = regmatch.endpos[0].col;
- line[matchcol] != NUL && matchcol < pos.col;
- ++matchcol)
- ;
+ matchcol < line_len && matchcol < pos.col;
+ matchcol++) {
+ }
+ }
- /* if the skip pattern includes end-of-line, break here */
- if (line[matchcol] == NUL)
+ // if the skip pattern includes end-of-line, break here
+ if (matchcol >= line_len) {
break;
+ }
- continue; /* start with first end pattern again */
+ continue; // start with first end pattern again
}
}
@@ -3004,14 +3012,19 @@ static void syn_cmd_spell(exarg_T *eap, int syncing)
return;
next = skiptowhite(arg);
- if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
+ if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) {
curwin->w_s->b_syn_spell = SYNSPL_TOP;
- else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
+ } else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10) {
curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
- else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
+ } else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7) {
curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
- else
+ } else {
EMSG2(_("E390: Illegal argument: %s"), arg);
+ return;
+ }
+
+ // assume spell checking changed, force a redraw
+ redraw_win_later(curwin, NOT_VALID);
}
/*
@@ -3281,17 +3294,28 @@ static void syn_cmd_off(exarg_T *eap, int syncing)
}
static void syn_cmd_onoff(exarg_T *eap, char *name)
+ FUNC_ATTR_NONNULL_ALL
{
- char buf[100];
-
+ did_syntax_onoff = true;
eap->nextcmd = check_nextcmd(eap->arg);
if (!eap->skip) {
- strcpy(buf, "so ");
+ char buf[100];
+ strncpy(buf, "so ", 4);
vim_snprintf(buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
do_cmdline_cmd(buf);
}
}
+void syn_maybe_on(void)
+{
+ if (!did_syntax_onoff) {
+ exarg_T ea;
+ ea.arg = (char_u *)"";
+ ea.skip = false;
+ syn_cmd_onoff(&ea, "syntax");
+ }
+}
+
/*
* Handle ":syntax [list]" command: list current syntax words.
*/
@@ -4183,12 +4207,16 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
break;
if (p[1] == NUL) {
EMSG2(_("E789: Missing ']': %s"), kw);
- kw = p + 2; /* skip over the NUL */
- break;
+ goto error;
}
if (p[1] == ']') {
- kw = p + 1; /* skip over the "]" */
- break;
+ if (p[2] != NUL) {
+ EMSG3(_("E890: trailing char after ']': %s]%s"),
+ kw, &p[2]);
+ goto error;
+ }
+ kw = p + 1;
+ break; // skip over the "]"
}
if (has_mbyte) {
int l = (*mb_ptr2len)(p + 1);
@@ -4203,6 +4231,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
}
}
+error:
xfree(keyword_copy);
xfree(syn_opt_arg.cont_in_list);
xfree(syn_opt_arg.next_list);
@@ -4455,12 +4484,10 @@ syn_cmd_region (
if (illegal || not_enough)
rest = NULL;
- /*
- * Must have a "start" and "end" pattern.
- */
- if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
- pat_ptrs[ITEM_END] == NULL)) {
- not_enough = TRUE;
+ // Must have a "start" and "end" pattern.
+ if (rest != NULL && (pat_ptrs[ITEM_START] == NULL
+ || pat_ptrs[ITEM_END] == NULL)) {
+ not_enough = true;
rest = NULL;
}
@@ -4843,9 +4870,10 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
int idx;
char_u *cpo_save;
- /* need at least three chars */
- if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
+ // need at least three chars
+ if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL) {
return NULL;
+ }
end = skip_regexp(arg + 1, *arg, TRUE, NULL);
if (*end != *arg) { /* end delimiter not found */
@@ -4982,6 +5010,10 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
curwin->w_s->b_syn_sync_maxlines = 0;
}
} else if (STRCMP(key, "LINECONT") == 0) {
+ if (*next_arg == NUL) { // missing pattern
+ illegal = true;
+ break;
+ }
if (curwin->w_s->b_syn_linecont_pat != NULL) {
EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
finished = TRUE;
@@ -5395,8 +5427,10 @@ void ex_ownsyntax(exarg_T *eap)
if (curwin->w_s == &curwin->w_buffer->b_s) {
curwin->w_s = xmalloc(sizeof(synblock_T));
memset(curwin->w_s, 0, sizeof(synblock_T));
- // TODO: Keep the spell checking as it was.
- curwin->w_p_spell = FALSE; /* No spell checking */
+ hash_init(&curwin->w_s->b_keywtab);
+ hash_init(&curwin->w_s->b_keywtab_ic);
+ // TODO: Keep the spell checking as it was. NOLINT(readability/todo)
+ curwin->w_p_spell = false; // No spell checking
clear_string_option(&curwin->w_s->b_p_spc);
clear_string_option(&curwin->w_s->b_p_spf);
clear_string_option(&curwin->w_s->b_p_spl);
@@ -5507,25 +5541,29 @@ char_u *get_syntax_name(expand_T *xp, int idx)
}
-/*
- * Function called for expression evaluation: get syntax ID at file position.
- */
-int
-syn_get_id (
+// Function called for expression evaluation: get syntax ID at file position.
+int syn_get_id(
win_T *wp,
long lnum,
colnr_T col,
- int trans, /* remove transparency */
- bool *spellp, /* return: can do spell checking */
- int keep_state /* keep state of char at "col" */
+ int trans, // remove transparency
+ bool *spellp, // return: can do spell checking
+ int keep_state // keep state of char at "col"
)
{
- /* When the position is not after the current position and in the same
- * line of the same buffer, need to restart parsing. */
+ // When the position is not after the current position and in the same
+ // line of the same buffer, need to restart parsing.
if (wp->w_buffer != syn_buf
|| lnum != current_lnum
- || col < current_col)
+ || col < current_col) {
syntax_start(wp, lnum);
+ } else if (wp->w_buffer == syn_buf
+ && lnum == current_lnum
+ && col > current_col) {
+ // next_match may not be correct when moving around, e.g. with the
+ // "skip" expression in searchpair()
+ next_match_idx = -1;
+ }
(void)get_syntax_attr(col, spellp, keep_state);
@@ -6133,12 +6171,11 @@ do_highlight (
break;
}
- /*
- * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
- * "guibg").
- */
- while (*linep && !ascii_iswhite(*linep) && *linep != '=')
- ++linep;
+ // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg",
+ // "guibg" or "guisp").
+ while (*linep && !ascii_iswhite(*linep) && *linep != '=') {
+ linep++;
+ }
xfree(key);
key = vim_strnsave_up(key_start, (int)(linep - key_start));
linep = skipwhite(linep);
@@ -6334,18 +6371,14 @@ do_highlight (
} else
HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
}
- color &= 7; /* truncate to 8 colors */
- } else if (t_colors == 16 || t_colors == 88 || t_colors == 256) {
- switch (t_colors) {
- case 16:
- color = color_numbers_8[i];
- break;
- case 88:
- color = color_numbers_88[i];
- break;
- case 256:
- color = color_numbers_256[i];
- break;
+ color &= 7; // truncate to 8 colors
+ } else if (t_colors == 16 || t_colors == 88 || t_colors >= 256) {
+ if (t_colors == 88) {
+ color = color_numbers_88[i];
+ } else if (t_colors >= 256) {
+ color = color_numbers_256[i];
+ } else {
+ color = color_numbers_8[i];
}
}
}
@@ -6365,7 +6398,7 @@ do_highlight (
HL_TABLE()[idx].sg_cterm_bg = color + 1;
if (is_normal_group) {
cterm_normal_bg_color = color + 1;
- {
+ if (!ui_rgb_attached()) {
must_redraw = CLEAR;
if (color >= 0) {
if (t_colors < 16)
@@ -6420,7 +6453,23 @@ do_highlight (
normal_bg = HL_TABLE()[idx].sg_rgb_bg;
}
} else if (STRCMP(key, "GUISP") == 0) {
- // Ignored for now
+ if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
+ if (!init)
+ HL_TABLE()[idx].sg_set |= SG_GUI;
+
+ xfree(HL_TABLE()[idx].sg_rgb_sp_name);
+ if (STRCMP(arg, "NONE") != 0) {
+ HL_TABLE()[idx].sg_rgb_sp_name = (uint8_t *)xstrdup((char *)arg);
+ HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg);
+ } else {
+ HL_TABLE()[idx].sg_rgb_sp_name = NULL;
+ HL_TABLE()[idx].sg_rgb_sp = -1;
+ }
+ }
+
+ if (is_normal_group) {
+ normal_sp = HL_TABLE()[idx].sg_rgb_sp;
+ }
} else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) {
// Ignored for now
} else {
@@ -6484,6 +6533,7 @@ void restore_cterm_colors(void)
{
normal_fg = -1;
normal_bg = -1;
+ normal_sp = -1;
cterm_normal_fg_color = 0;
cterm_normal_fg_bold = 0;
cterm_normal_bg_color = 0;
@@ -6500,6 +6550,7 @@ static int hl_has_settings(int idx, int check_link)
|| HL_TABLE()[idx].sg_cterm_bg != 0
|| HL_TABLE()[idx].sg_rgb_fg_name != NULL
|| HL_TABLE()[idx].sg_rgb_bg_name != NULL
+ || HL_TABLE()[idx].sg_rgb_sp_name != NULL
|| (check_link && (HL_TABLE()[idx].sg_set & SG_LINK));
}
@@ -6516,14 +6567,18 @@ static void highlight_clear(int idx)
HL_TABLE()[idx].sg_gui = 0;
HL_TABLE()[idx].sg_rgb_fg = -1;
HL_TABLE()[idx].sg_rgb_bg = -1;
+ HL_TABLE()[idx].sg_rgb_sp = -1;
xfree(HL_TABLE()[idx].sg_rgb_fg_name);
HL_TABLE()[idx].sg_rgb_fg_name = NULL;
xfree(HL_TABLE()[idx].sg_rgb_bg_name);
HL_TABLE()[idx].sg_rgb_bg_name = NULL;
- /* Clear the script ID only when there is no link, since that is not
- * cleared. */
- if (HL_TABLE()[idx].sg_link == 0)
+ xfree(HL_TABLE()[idx].sg_rgb_sp_name);
+ HL_TABLE()[idx].sg_rgb_sp_name = NULL;
+ // Clear the script ID only when there is no link, since that is not
+ // cleared.
+ if (HL_TABLE()[idx].sg_link == 0) {
HL_TABLE()[idx].sg_scriptID = 0;
+ }
}
@@ -6565,7 +6620,8 @@ int get_attr_entry(attrentry_T *aep)
&& aep->cterm_bg_color == taep->cterm_bg_color
&& aep->rgb_ae_attr == taep->rgb_ae_attr
&& aep->rgb_fg_color == taep->rgb_fg_color
- && aep->rgb_bg_color == taep->rgb_bg_color) {
+ && aep->rgb_bg_color == taep->rgb_bg_color
+ && aep->rgb_sp_color == taep->rgb_sp_color) {
return i + ATTR_OFF;
}
}
@@ -6603,6 +6659,7 @@ int get_attr_entry(attrentry_T *aep)
taep->rgb_ae_attr = aep->rgb_ae_attr;
taep->rgb_fg_color = aep->rgb_fg_color;
taep->rgb_bg_color = aep->rgb_bg_color;
+ taep->rgb_sp_color = aep->rgb_sp_color;
return table->ga_len - 1 + ATTR_OFF;
}
@@ -6664,6 +6721,10 @@ int hl_combine_attr(int char_attr, int prim_attr)
if (spell_aep->rgb_bg_color >= 0) {
new_en.rgb_bg_color = spell_aep->rgb_bg_color;
}
+
+ if (spell_aep->rgb_sp_color >= 0) {
+ new_en.rgb_sp_color = spell_aep->rgb_sp_color;
+ }
}
return get_attr_entry(&new_en);
}
@@ -6701,7 +6762,7 @@ static void highlight_list_one(int id)
didh = highlight_list_arg(id, didh, LIST_STRING,
0, sgp->sg_rgb_bg_name, "guibg");
didh = highlight_list_arg(id, didh, LIST_STRING,
- 0, NULL, "guisp");
+ 0, sgp->sg_rgb_sp_name, "guisp");
if (sgp->sg_link && !got_int) {
(void)syn_list_header(didh, 9999, id);
@@ -6815,8 +6876,9 @@ highlight_color (
if (modec == 'g') {
if (fg)
return HL_TABLE()[id - 1].sg_rgb_fg_name;
- if (sp)
- return NULL;
+ if (sp) {
+ return HL_TABLE()[id - 1].sg_rgb_sp_name;
+ }
return HL_TABLE()[id - 1].sg_rgb_bg_name;
}
if (font || sp)
@@ -6903,7 +6965,17 @@ set_hl_attr (
// before setting attr_entry->{f,g}g_color to a other than -1
at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1;
at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1;
- sgp->sg_attr = get_attr_entry(&at_en);
+ at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1;
+
+ if (at_en.cterm_fg_color != 0 || at_en.cterm_bg_color != 0
+ || at_en.rgb_fg_color != -1 || at_en.rgb_bg_color != -1
+ || at_en.rgb_sp_color != -1 || at_en.cterm_ae_attr != 0
+ || at_en.rgb_ae_attr != 0) {
+ sgp->sg_attr = get_attr_entry(&at_en);
+ } else {
+ // If all the fields are cleared, clear the attr field back to default value
+ sgp->sg_attr = 0;
+ }
}
/*
@@ -7234,6 +7306,10 @@ int highlight_changed(void)
hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg;
}
+ if (hlt[id - 1].sg_rgb_sp != hlt[id_S - 1].sg_rgb_sp) {
+ hlt[hlcnt + i].sg_rgb_sp = hlt[id - 1].sg_rgb_sp;
+ }
+
highlight_ga.ga_len = hlcnt + i + 1;
set_hl_attr(hlcnt + i); /* At long last we can apply */
highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h
index 67cf672ef2..8d207e6286 100644
--- a/src/nvim/syntax_defs.h
+++ b/src/nvim/syntax_defs.h
@@ -69,8 +69,8 @@ struct syn_state {
// Structure shared between syntax.c, screen.c
typedef struct attr_entry {
- short rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc.
- RgbValue rgb_fg_color, rgb_bg_color;
+ int16_t rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc.
+ RgbValue rgb_fg_color, rgb_bg_color, rgb_sp_color;
int cterm_fg_color, cterm_bg_color;
} attrentry_T;
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 8fcb02c3b6..7885d467d8 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -526,19 +526,17 @@ do_tag (
taglen_advance(taglen);
MSG_PUTS_ATTR(_("file\n"), hl_attr(HLF_T));
- for (i = 0; i < num_matches && !got_int; ++i) {
+ for (i = 0; i < num_matches && !got_int; i++) {
parse_match(matches[i], &tagp);
- if (!new_tag && (
- (g_do_tagpreview != 0
- && i == ptag_entry.cur_match) ||
- (use_tagstack
- && i == tagstack[tagstackidx].cur_match)))
+ if (!new_tag && ((g_do_tagpreview != 0 && i == ptag_entry.cur_match)
+ || (use_tagstack
+ && i == tagstack[tagstackidx].cur_match))) {
*IObuff = '>';
- else
+ } else {
*IObuff = ' ';
- vim_snprintf((char *)IObuff + 1, IOSIZE - 1,
- "%2d %s ", i + 1,
- mt_names[matches[i][0] & MT_MASK]);
+ }
+ vim_snprintf((char *)IObuff + 1, IOSIZE - 1, "%2d %s ", i + 1,
+ mt_names[matches[i][0] & MT_MASK]);
msg_puts(IObuff);
if (tagp.tagkind != NULL)
msg_outtrans_len(tagp.tagkind,
@@ -872,7 +870,7 @@ do_tag (
/* Let the SwapExists event know what tag we are jumping to. */
vim_snprintf((char *)IObuff, IOSIZE, ":ta %s\r", name);
- set_vim_var_string(VV_SWAPCOMMAND, IObuff, -1);
+ set_vim_var_string(VV_SWAPCOMMAND, (char *) IObuff, -1);
/*
* Jump to the desired match.
@@ -1147,6 +1145,22 @@ find_tags (
int get_it_again = FALSE;
int use_cscope = (flags & TAG_CSCOPE);
int verbose = (flags & TAG_VERBOSE);
+ int save_p_ic = p_ic;
+
+ // Change the value of 'ignorecase' according to 'tagcase' for the
+ // duration of this function.
+ switch (curbuf->b_tc_flags ? curbuf->b_tc_flags : tc_flags) {
+ case TC_FOLLOWIC:
+ break;
+ case TC_IGNORE:
+ p_ic = true;
+ break;
+ case TC_MATCH:
+ p_ic = false;
+ break;
+ default:
+ assert(false);
+ }
help_save = curbuf->b_help;
orgpat.pat = pat;
@@ -1210,20 +1224,15 @@ find_tags (
for (round = 1; round <= 2; ++round) {
linear = (orgpat.headlen == 0 || !p_tbs || round == 2);
- /*
- * Try tag file names from tags option one by one.
- */
- for (first_file = TRUE;
- use_cscope ||
- get_tagfname(&tn, first_file, tag_fname) == OK;
- first_file = FALSE) {
- /*
- * A file that doesn't exist is silently ignored. Only when not a
- * single file is found, an error message is given (further on).
- */
- if (use_cscope)
- fp = NULL; /* avoid GCC warning */
- else {
+ // Try tag file names from tags option one by one.
+ for (first_file = true;
+ use_cscope || get_tagfname(&tn, first_file, tag_fname) == OK;
+ first_file = false) {
+ // A file that doesn't exist is silently ignored. Only when not a
+ // single file is found, an error message is given (further on).
+ if (use_cscope) {
+ fp = NULL; // avoid GCC warning
+ } else {
if (curbuf->b_help) {
/* Prefer help tags according to 'helplang'. Put the
* two-letter language name in help_lang[]. */
@@ -1955,6 +1964,8 @@ findtag_end:
curbuf->b_help = help_save;
xfree(saved_pat);
+ p_ic = save_p_ic;
+
return retval;
}
diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c
deleted file mode 100644
index a218c03fdb..0000000000
--- a/src/nvim/tempfile.c
+++ /dev/null
@@ -1,138 +0,0 @@
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "nvim/ascii.h"
-#include "nvim/memory.h"
-#include "nvim/misc1.h"
-#include "nvim/os/os.h"
-#include "nvim/os/os_defs.h"
-#include "nvim/path.h"
-#include "nvim/strings.h"
-#include "nvim/tempfile.h"
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "tempfile.c.generated.h"
-#endif
-
-/// Name of Vim's own temp dir. Ends in a slash.
-static char_u *vim_tempdir = NULL;
-
-/// Create a directory for private use by this instance of Neovim.
-/// This is done once, and the same directory is used for all temp files.
-/// This method avoids security problems because of symlink attacks et al.
-/// It's also a bit faster, because we only need to check for an existing
-/// file when creating the directory and not for each temp file.
-static void vim_maketempdir(void)
-{
- static const char *temp_dirs[] = TEMP_DIR_NAMES;
- // Try the entries in `TEMP_DIR_NAMES` to create the temp directory.
- char_u template[TEMP_FILE_PATH_MAXLEN];
- char_u path[TEMP_FILE_PATH_MAXLEN];
- for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); ++i) {
- // Expand environment variables, leave room for "/nvimXXXXXX/999999999"
- expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22);
- if (!os_isdir(template)) { // directory doesn't exist
- continue;
- }
-
- add_pathsep((char *)template);
- // Concatenate with temporary directory name pattern
- STRCAT(template, "nvimXXXXXX");
-
- if (os_mkdtemp((const char *)template, (char *)path) != 0) {
- continue;
- }
-
- if (vim_settempdir((char *)path)) {
- // Successfully created and set temporary directory so stop trying.
- break;
- } else {
- // Couldn't set `vim_tempdir` to `path` so remove created directory.
- os_rmdir((char *)path);
- }
- }
-}
-
-/// Delete the temp directory and all files it contains.
-void vim_deltempdir(void)
-{
- if (vim_tempdir != NULL) {
- snprintf((char *)NameBuff, MAXPATHL, "%s*", vim_tempdir);
-
- char_u **files;
- int file_count;
-
- // Note: We cannot just do `&NameBuff` because it is a statically
- // sized array so `NameBuff == &NameBuff` according to C semantics.
- char_u *buff_list[1] = {NameBuff};
- if (gen_expand_wildcards(1, buff_list, &file_count, &files,
- EW_DIR|EW_FILE|EW_SILENT) == OK) {
- for (int i = 0; i < file_count; ++i) {
- os_remove((char *)files[i]);
- }
- FreeWild(file_count, files);
- }
- path_tail(NameBuff)[-1] = NUL;
- os_rmdir((char *)NameBuff);
-
- xfree(vim_tempdir);
- vim_tempdir = NULL;
- }
-}
-
-/// Get the name of temp directory. This directory would be created on the first
-/// call to this function.
-char_u *vim_gettempdir(void)
-{
- if (vim_tempdir == NULL) {
- vim_maketempdir();
- }
-
- return vim_tempdir;
-}
-
-/// Set Neovim own temporary directory name to `tempdir`. This directory should
-/// be already created. Expand this name to a full path and put it in
-/// `vim_tempdir`. This avoids that using `:cd` would confuse us.
-///
-/// @param tempdir must be no longer than MAXPATHL.
-///
-/// @return false if we run out of memory.
-static bool vim_settempdir(char *tempdir)
-{
- char *buf = verbose_try_malloc(MAXPATHL + 2);
- if (!buf) {
- return false;
- }
- vim_FullName(tempdir, buf, MAXPATHL, false);
- add_pathsep(buf);
- vim_tempdir = (char_u *)xstrdup(buf);
- xfree(buf);
- return true;
-}
-
-/// Return a unique name that can be used for a temp file.
-///
-/// @note The temp file is NOT created.
-///
-/// @return pointer to the temp file name or NULL if Neovim can't create
-/// temporary directory for its own temporary files.
-char_u *vim_tempname(void)
-{
- // Temp filename counter.
- static uint32_t temp_count;
-
- char_u *tempdir = vim_gettempdir();
- if (!tempdir) {
- return NULL;
- }
-
- // There is no need to check if the file exists, because we own the directory
- // and nobody else creates a file in it.
- char_u template[TEMP_FILE_PATH_MAXLEN];
- snprintf((char *)template, TEMP_FILE_PATH_MAXLEN,
- "%s%" PRIu32, tempdir, temp_count++);
- return vim_strsave(template);
-}
diff --git a/src/nvim/tempfile.h b/src/nvim/tempfile.h
deleted file mode 100644
index c030a70eeb..0000000000
--- a/src/nvim/tempfile.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef NVIM_TEMPFILE_H
-#define NVIM_TEMPFILE_H
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "tempfile.h.generated.h"
-#endif
-
-#endif // NVIM_TEMPFILE_H
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 0a7807d811..104cc47cda 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -141,10 +141,6 @@ struct terminal {
int pressed_button;
// pending width/height
bool pending_resize;
- // color palette. this isn't set directly in the vterm instance because
- // the default values are used to obtain the color numbers passed to cterm
- // colors
- RgbValue colors[256];
// With a reference count of 0 the terminal can be freed.
size_t refcount;
};
@@ -179,7 +175,16 @@ void terminal_init(void)
for (int color_index = 0; color_index < 256; color_index++) {
VTermColor color;
- vterm_state_get_palette_color(state, color_index, &color);
+ // Some of the default 16 colors has the same color as the later
+ // 240 colors. To avoid collisions, we will use the custom colors
+ // below in non true color mode.
+ if (color_index < 16) {
+ color.red = 0;
+ color.green = 0;
+ color.blue = (uint8_t)(color_index + 1);
+ } else {
+ vterm_state_get_palette_color(state, color_index, &color);
+ }
map_put(int, int)(color_indexes,
RGB(color.red, color.green, color.blue), color_index + 1);
}
@@ -205,6 +210,7 @@ void terminal_teardown(void)
Terminal *terminal_open(TerminalOptions opts)
{
+ bool true_color = ui_rgb_attached();
// Create a new terminal instance and configure it
Terminal *rv = xcalloc(1, sizeof(Terminal));
rv->opts = opts;
@@ -220,7 +226,7 @@ Terminal *terminal_open(TerminalOptions opts)
// Set up screen
rv->vts = vterm_obtain_screen(rv->vt);
vterm_screen_enable_altscreen(rv->vts, true);
- // delete empty lines at the end of the buffer
+ // delete empty lines at the end of the buffer
vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv);
vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL);
vterm_screen_reset(rv->vts, 1);
@@ -236,7 +242,7 @@ Terminal *terminal_open(TerminalOptions opts)
set_option_value((uint8_t *)"relativenumber", false, NULL, OPT_LOCAL);
RESET_BINDING(curwin);
// Apply TermOpen autocmds so the user can configure the terminal
- apply_autocmds(EVENT_TERMOPEN, NULL, NULL, true, curbuf);
+ apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf);
// Configure the scrollback buffer. Try to get the size from:
//
@@ -250,12 +256,27 @@ Terminal *terminal_open(TerminalOptions opts)
rv->sb_size = MIN(rv->sb_size, 100000);
rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size);
+ if (!true_color) {
+ // Change the first 16 colors so we can easily get the correct color
+ // index from them.
+ for (int i = 0; i < 16; i++) {
+ VTermColor color;
+ color.red = 0;
+ color.green = 0;
+ color.blue = (uint8_t)(i + 1);
+ vterm_state_set_palette_color(state, i, &color);
+ }
+ return rv;
+ }
+
+ vterm_state_set_bold_highbright(state, true);
+
// Configure the color palette. Try to get the color from:
//
// - b:terminal_color_{NUM}
// - g:terminal_color_{NUM}
// - the VTerm instance
- for (int i = 0; i < (int)ARRAY_SIZE(rv->colors); i++) {
+ for (int i = 0; i < 16; i++) {
RgbValue color_val = -1;
char var[64];
snprintf(var, sizeof(var), "terminal_color_%d", i);
@@ -265,16 +286,13 @@ Terminal *terminal_open(TerminalOptions opts)
xfree(name);
if (color_val != -1) {
- rv->colors[i] = color_val;
+ VTermColor color;
+ color.red = (uint8_t)((color_val >> 16) & 0xFF);
+ color.green = (uint8_t)((color_val >> 8) & 0xFF);
+ color.blue = (uint8_t)((color_val >> 0) & 0xFF);
+ vterm_state_set_palette_color(state, i, &color);
}
}
-
- if (color_val == -1) {
- // the default is taken from vterm
- VTermColor color;
- vterm_state_get_palette_color(state, i, &color);
- rv->colors[i] = RGB(color.red, color.green, color.blue);
- }
}
return rv;
@@ -288,8 +306,9 @@ void terminal_close(Terminal *term, char *msg)
term->forward_mouse = false;
term->closed = true;
+ buf_T *buf = handle_get_buffer(term->buf_handle);
+
if (!msg || exiting) {
- buf_T *buf = handle_get_buffer(term->buf_handle);
// If no msg was given, this was called by close_buffer(buffer.c). Or if
// exiting, we must inform the buffer the terminal no longer exists so that
// close_buffer() doesn't call this again.
@@ -304,6 +323,10 @@ void terminal_close(Terminal *term, char *msg)
} else {
terminal_receive(term, msg, strlen(msg));
}
+
+ if (buf) {
+ apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf);
+ }
}
void terminal_resize(Terminal *term, uint16_t width, uint16_t height)
@@ -406,6 +429,10 @@ static int terminal_execute(VimState *state, int key)
apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
break;
+ // Temporary fix until paste events gets implemented
+ case K_PASTE:
+ break;
+
case K_LEFTMOUSE:
case K_LEFTDRAG:
case K_LEFTRELEASE:
@@ -539,14 +566,10 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr,
// Since libvterm does not expose the color index used by the program, we
// use the rgb value to find the appropriate index in the cache computed by
// `terminal_init`.
- int vt_fg_idx = vt_fg != default_vt_fg ?
+ int vt_fg_idx = vt_fg != -1 ?
map_get(int, int)(color_indexes, vt_fg) : 0;
- int vt_bg_idx = vt_bg != default_vt_bg ?
+ int vt_bg_idx = vt_bg != -1 ?
map_get(int, int)(color_indexes, vt_bg) : 0;
- // The index is now used to get the final rgb value from the
- // user-customizable palette.
- int vt_fg_rgb = vt_fg_idx != 0 ? term->colors[vt_fg_idx - 1] : -1;
- int vt_bg_rgb = vt_bg_idx != 0 ? term->colors[vt_bg_idx - 1] : -1;
int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0)
| (cell.attrs.italic ? HL_ITALIC : 0)
@@ -561,8 +584,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr,
.cterm_fg_color = vt_fg_idx,
.cterm_bg_color = vt_bg_idx,
.rgb_ae_attr = (int16_t)hl_attrs,
- .rgb_fg_color = vt_fg_rgb,
- .rgb_bg_color = vt_bg_rgb,
+ .rgb_fg_color = vt_fg,
+ .rgb_bg_color = vt_bg,
});
}
@@ -622,6 +645,7 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
api_free_object(dict_set_value(buf->b_vars,
cstr_as_string("term_title"),
STRING_OBJ(cstr_as_string(val->string)),
+ false,
&err));
break;
}
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index d1a7abfbf7..867cab9fbf 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -7,30 +7,41 @@ export SHELL := sh
VIMPROG := ../../../build/bin/nvim
SCRIPTSOURCE := ../../../runtime
-SCRIPTS := \
- test8.out test10.out \
- test11.out test12.out test13.out test14.out \
- test17.out \
- test24.out \
- test30.out \
- test32.out test34.out \
- test36.out test37.out test40.out \
- test42.out \
- test47.out test48.out test49.out \
- test52.out test53.out test55.out \
- test64.out \
- test68.out test69.out \
- test73.out \
- test79.out \
- test88.out \
- test_listlbr.out \
- test_breakindent.out \
- test_charsearch.out \
- test_close_count.out \
- test_command_count.out \
- test_marks.out \
-
-NEW_TESTS =
+SCRIPTS := \
+ test8.out \
+ test10.out \
+ test12.out \
+ test13.out \
+ test14.out \
+ test17.out \
+ test24.out \
+ test30.out \
+ test32.out \
+ test34.out \
+ test37.out \
+ test40.out \
+ test42.out \
+ test47.out \
+ test48.out \
+ test49.out \
+ test52.out \
+ test53.out \
+ test55.out \
+ test64.out \
+ test69.out \
+ test73.out \
+ test79.out \
+ test_marks.out \
+
+# Tests using runtest.vim.vim.
+# Keep test_alot*.res as the last one, sort the others.
+NEW_TESTS = \
+ test_cursor_func.res \
+ test_help_tagjump.res \
+ test_menu.res \
+ test_timers.res \
+ test_viml.res \
+ test_alot.res
SCRIPTS_GUI := test16.out
@@ -86,7 +97,7 @@ test1.out: $(VIMPROG)
$(SCRIPTS) $(SCRIPTS_GUI): $(VIMPROG) test1.out
RM_ON_RUN := test.out X* viminfo
-RM_ON_START := tiny.vim small.vim mbyte.vim test.ok
+RM_ON_START := test.ok
RUN_VIM := VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(TOOL) $(VIMPROG) -u unix.vim -U NONE -i viminfo --noplugin -s dotest.in
clean:
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index 8314a45d0c..2712fb9371 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -20,9 +20,6 @@
" If cleanup after each Test_ function is needed, define a TearDown function.
" It will be called after each Test_ function.
-" Without the +eval feature we can't run these tests, bail out.
-so small.vim
-
" Check that the screen size is at least 24 x 80 characters.
if &lines < 24 || &columns < 80
let error = 'Screen size too small! Tests require at least 24 lines with 80 characters'
@@ -33,6 +30,15 @@ if &lines < 24 || &columns < 80
cquit
endif
+" This also enables use of line continuation.
+set viminfo+=nviminfo
+
+" Avoid stopping at the "hit enter" prompt
+set nomore
+
+" Output all messages in English.
+lang mess C
+
" Source the test script. First grab the file name, in case the script
" navigates away.
let testname = expand('%')
@@ -40,20 +46,27 @@ let done = 0
let fail = 0
let errors = []
let messages = []
-try
+if expand('%') =~ 'test_viml.vim'
+ " this test has intentional errors, don't use try/catch.
source %
-catch
- let fail += 1
- call add(errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint)
-endtry
+else
+ try
+ source %
+ catch
+ let fail += 1
+ call add(errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint)
+ endtry
+endif
" Locate Test_ functions and execute them.
+set nomore
redir @q
function /^Test_
redir END
let tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g'))
-for test in tests
+" Execute the tests in alphabetical order.
+for test in sort(tests)
if exists("*SetUp")
call SetUp()
endif
diff --git a/src/nvim/testdir/test1.in b/src/nvim/testdir/test1.in
index 85ff1b4db2..272500cd25 100644
--- a/src/nvim/testdir/test1.in
+++ b/src/nvim/testdir/test1.in
@@ -1,20 +1,5 @@
-
First a simple test to check if the test script works.
-If Vim was not compiled with the +eval feature, the small.vim script will be
-set to copy the test.ok file to test.out, so that it looks like the test
-succeeded. Otherwise an empty small.vim is written. small.vim is sourced by
-tests that require the +eval feature or other features that are missing in the
-small version.
-
-If Vim was not compiled with the +windows feature, the tiny.vim script will be
-set like small.vim above. tiny.vim is sourced by tests that require the
-+windows feature or other features that are missing in the tiny version.
-
-If Vim was not compiled with the +multi_byte feature, the mbyte.vim script will
-be set like small.vim above. mbyte.vim is sourced by tests that require the
-+multi_byte feature.
-
STARTTEST
:" If columns or lines are too small, create wrongtermsize.
:" (Some tests will fail. When columns and/or lines are small)
@@ -23,25 +8,6 @@ STARTTEST
:" Write a single line to test.out to check if testing works at all.
:%d
athis is a test:w! test.out
-:" Create small.vim and tiny.vim empty, create mbyte.vim to skip the test.
-0D:w! small.vim
-:w! tiny.vim
-ae! test.ok
-w! test.out
-qa!
-:w! mbyte.vim
-:"
-:" If +multi_byte feature supported, make mbyte.vim empty.
-:if has("multi_byte") | sp another | w! mbyte.vim | q | endif
-:"
-:" If +eval feature supported quit here, leaving tiny.vim and small.vim empty.
-:" Otherwise write small.vim to skip the test.
-:if 1 | q! | endif
-:w! small.vim
-:" If +windows feature not supported :sp will fail and tiny.vim will be
-:" written to skip the test.
-:sp another
-:wq! tiny.vim
:qa!
ENDTEST
diff --git a/src/nvim/testdir/test10.in b/src/nvim/testdir/test10.in
index 769d690acb..2178cf41ce 100644
--- a/src/nvim/testdir/test10.in
+++ b/src/nvim/testdir/test10.in
@@ -1,9 +1,6 @@
Test for 'errorformat'. This will fail if the quickfix feature was disabled.
STARTTEST
-:so small.vim
-:" Also test a BOM is ignored.
-:so mbyte.vim
:7/start of errorfile/,/end of errorfile/w! Xerrorfile1
:7/start of errorfile/,/end of errorfile/-1w! Xerrorfile2
:/start of testfile/,/end of testfile/w! Xtestfile
diff --git a/src/nvim/testdir/test10a.in b/src/nvim/testdir/test10a.in
index 19e8652fe5..99a5a03db8 100644
--- a/src/nvim/testdir/test10a.in
+++ b/src/nvim/testdir/test10a.in
@@ -1,7 +1,6 @@
Test for 'errorformat'.
STARTTEST
-:so small.vim
:/start of errorfile/,/end of errorfile/w! Xerrorfile
:/start of testfile/,/end of testfile/w! Xtestfile
:cf Xerrorfile
diff --git a/src/nvim/testdir/test11.in b/src/nvim/testdir/test11.in
deleted file mode 100644
index 9e9e257c1d..0000000000
--- a/src/nvim/testdir/test11.in
+++ /dev/null
@@ -1,84 +0,0 @@
-Tests for autocommands:
-- FileWritePre writing a compressed file
-- FileReadPost reading a compressed file
-- BufNewFile reading a file template
-- BufReadPre decompressing the file to be read
-- FilterReadPre substituting characters in the temp file
-- FilterReadPost substituting characters after filtering
-- FileReadPre set options for decompression
-- FileReadPost decompress the file
-
-Note: This test is skipped if "gzip" is not available.
-$GZIP is made empty, "-v" would cause trouble.
-Use a FileChangedShell autocommand to avoid a prompt for "Xtestfile.gz" being
-modified outside of Vim (noticed on Solaris).
-
-STARTTEST
-:so small.vim
-:" drop out when there is no gzip program
-:if !executable("gzip")
-: e! test.ok
-: w! test.out
-: qa!
-:endif
-:let $GZIP = ""
-:au FileChangedShell * echo "caught FileChangedShell"
-:set bin
-:au FileWritePre *.gz '[,']!gzip
-:au FileWritePost *.gz undo
-:/^start of testfile/,/^end of testfile/w! Xtestfile.gz
-:au FileReadPost *.gz '[,']!gzip -d
-:$r Xtestfile.gz " Read and decompress the testfile
-:?startstart?,$w! test.out " Write contents of this file
-:au BufNewFile *.c read Xtest.c
-:/^start of test.c/+1,/^end of test.c/-1w! Xtest.c
-:e! foo.c " Will load Xtest.c
-:au FileAppendPre *.out '[,']s/new/NEW/
-:au FileAppendPost *.out !cat Xtest.c >>test.out
-:w>>test.out " Append it to the output file
-:au! FileAppendPre
-:" setup autocommands to decompress before reading and re-compress afterwards
-:au BufReadPre *.gz exe '!gzip -d ' . shellescape(expand("<afile>"))
-:au BufReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>"))
-:au BufReadPost *.gz call rename(expand("<afile>"), expand("<afile>:r"))
-:au BufReadPost *.gz exe '!gzip ' . shellescape(expand("<afile>:r"))
-:e! Xtestfile.gz " Edit compressed file
-:w>>test.out " Append it to the output file
-:set shelltemp " need temp files here
-:au FilterReadPre *.out call rename(expand("<afile>"), expand("<afile>") . ".t")
-:au FilterReadPre *.out exe 'silent !sed s/e/E/ ' . shellescape(expand("<afile>")) . ".t >" . shellescape(expand("<afile>"))
-:au FilterReadPre *.out exe 'silent !rm ' . shellescape(expand("<afile>")) . '.t'
-:au FilterReadPost *.out '[,']s/x/X/g
-:e! test.out " Edit the output file
-:23,$!cat
-:23,$s/\r$// " remove CR for when sed adds them
-:au! FileReadPre *.gz exe 'silent !gzip -d ' . shellescape(expand("<afile>"))
-:au FileReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>"))
-:au! FileReadPost *.gz '[,']s/l/L/
-:$r Xtestfile.gz " Read compressed file
-:w " write it, after filtering
-:au! " remove all autocommands
-:e " Edit test.out again
-:set nobin ff& " use the default fileformat for writing
-:w
-:qa!
-ENDTEST
-
-startstart
-start of testfile
-line 2 Abcdefghijklmnopqrstuvwxyz
-line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-line 4 Abcdefghijklmnopqrstuvwxyz
-line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-line 6 Abcdefghijklmnopqrstuvwxyz
-line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-line 8 Abcdefghijklmnopqrstuvwxyz
-line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-line 10 Abcdefghijklmnopqrstuvwxyz
-end of testfile
-
-start of test.c
-/*
- * Here is a new .c file
- */
-end of test.c
diff --git a/src/nvim/testdir/test11.ok b/src/nvim/testdir/test11.ok
deleted file mode 100644
index af8c5ce261..0000000000
--- a/src/nvim/testdir/test11.ok
+++ /dev/null
@@ -1,61 +0,0 @@
-startstart
-start of testfile
-line 2 Abcdefghijklmnopqrstuvwxyz
-line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-line 4 Abcdefghijklmnopqrstuvwxyz
-line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-line 6 Abcdefghijklmnopqrstuvwxyz
-line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-line 8 Abcdefghijklmnopqrstuvwxyz
-line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-line 10 Abcdefghijklmnopqrstuvwxyz
-end of testfile
-
-start of test.c
-/*
- * Here is a new .c file
- */
-end of test.c
-start of testfile
-line 2 Abcdefghijklmnopqrstuvwxyz
-line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-line 4 Abcdefghijklmnopqrstuvwxyz
-linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-linE 6 AbcdefghijklmnopqrstuvwXyz
-linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-linE 8 AbcdefghijklmnopqrstuvwXyz
-linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-linE 10 AbcdefghijklmnopqrstuvwXyz
-End of testfile
-
-/*
- * HEre is a NEW .c file
- */
-/*
- * HEre is a new .c file
- */
-start of tEstfile
-linE 2 AbcdefghijklmnopqrstuvwXyz
-linE 3 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-linE 4 AbcdefghijklmnopqrstuvwXyz
-linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-linE 6 AbcdefghijklmnopqrstuvwXyz
-linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-linE 8 AbcdefghijklmnopqrstuvwXyz
-linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-linE 10 AbcdefghijklmnopqrstuvwXyz
-End of testfile
-/*
- * HEre is a new .c file
- */
-start of testfiLe
-Line 2 Abcdefghijklmnopqrstuvwxyz
-Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-Line 4 Abcdefghijklmnopqrstuvwxyz
-Line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-Line 6 Abcdefghijklmnopqrstuvwxyz
-Line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-Line 8 Abcdefghijklmnopqrstuvwxyz
-Line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-Line 10 Abcdefghijklmnopqrstuvwxyz
-end of testfiLe
diff --git a/src/nvim/testdir/test12.in b/src/nvim/testdir/test12.in
index be3169a625..0c0623e5d4 100644
--- a/src/nvim/testdir/test12.in
+++ b/src/nvim/testdir/test12.in
@@ -4,7 +4,6 @@ Tests for 'directory' option.
- "dir", in directory relative to current dir
STARTTEST
-:so small.vim
:set dir=.,~
:/start of testfile/,/end of testfile/w! Xtest1
:" do an ls of the current dir to find the swap file (should not be there)
diff --git a/src/nvim/testdir/test13.in b/src/nvim/testdir/test13.in
index 6713f80e88..fa9ba312b7 100644
--- a/src/nvim/testdir/test13.in
+++ b/src/nvim/testdir/test13.in
@@ -11,7 +11,6 @@ Also test changing buffers in a BufDel autocommand. If this goes wrong there
are ml_line errors and/or a Crash.
STARTTEST
-:so small.vim
:/^start of testfile/,/^end of testfile/w! Xtestje1
:/^start of testfile/,/^end of testfile/w! Xtestje2
:/^start of testfile/,/^end of testfile/w! Xtestje3
diff --git a/src/nvim/testdir/test14.in b/src/nvim/testdir/test14.in
index 6ebec99af6..bef2e45431 100644
--- a/src/nvim/testdir/test14.in
+++ b/src/nvim/testdir/test14.in
@@ -5,7 +5,6 @@ Also test "[m", "]m", "[M" and "]M"
Also test search()
STARTTEST
-:so small.vim
/Start cursor here
vaBiBD:?Bug?,/Piece/-2w! test.out
/^- Bug
diff --git a/src/nvim/testdir/test17.in b/src/nvim/testdir/test17.in
index 7fef87d383..83abe17770 100644
--- a/src/nvim/testdir/test17.in
+++ b/src/nvim/testdir/test17.in
@@ -3,7 +3,6 @@ Tests for:
- ":checkpath!" with various 'include' settings.
STARTTEST
-:so small.vim
:set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,}
:function! DeleteDirectory(dir)
: if has("win16") || has("win32") || has("win64") || has("dos16") || has("dos32")
@@ -41,17 +40,17 @@ STARTTEST
:!mkdir Xdir1
:!mkdir "Xdir1/dir2"
:e! Xdir1/dir2/foo.a
-i#include "bar.a"
+i#include "bar.a":
:w
:e Xdir1/dir2/bar.a
-i#include "baz.a"
+i#include "baz.a":
:w
:e Xdir1/dir2/baz.a
-i#include "foo.a"
+i#include "foo.a":
:w
:e Xbase.a
:set path=Xdir1/dir2
-i#include <foo.a>
+i#include <foo.a>:
:w
:redir! >>test.out
:checkpath!
@@ -71,17 +70,17 @@ STARTTEST
:endfunction
:let &includeexpr='DotsToSlashes()'
:e! Xdir1/dir2/foo.b
-i%inc /bar/
+i%inc /bar/:
:w
:e Xdir1/dir2/bar.b
-i%inc /baz/
+i%inc /baz/:
:w
:e Xdir1/dir2/baz.b
-i%inc /foo/
+i%inc /foo/:
:w
:e Xbase.b
:set path=Xdir1/dir2
-i%inc /foo/
+i%inc /foo/:
:w
:redir! >>test.out
:checkpath!
@@ -104,20 +103,20 @@ STARTTEST
:endfunction
:let &includeexpr='StripNewlineChar()'
:e! Xdir1/dir2/foo.c
-i%inc bar.c
+i%inc bar.c:
:w
:e Xdir1/dir2/bar.c
-i%inc baz.c
+i%inc baz.c:
:w
:e Xdir1/dir2/baz.c
-i%inc foo.c
+i%inc foo.c:
:w
:e Xdir1/dir2/FALSE.c
-i%inc foo.c
+i%inc foo.c:
:w
:e Xbase.c
:set path=Xdir1/dir2
-i%inc FALSE.c foo.c
+i%inc FALSE.c foo.c:
:w
:redir! >>test.out
:checkpath!
diff --git a/src/nvim/testdir/test30.in b/src/nvim/testdir/test30.in
index 2a89eac73d..56d5d5c6c2 100644
--- a/src/nvim/testdir/test30.in
+++ b/src/nvim/testdir/test30.in
@@ -3,7 +3,6 @@ Test for a lot of variations of the 'fileformats' option
Note: This test will fail if "cat" is not available.
STARTTEST
-:so small.vim
:" first write three test files, one in each format
:set fileformat=unix
:set fileformats=
diff --git a/src/nvim/testdir/test32.in b/src/nvim/testdir/test32.in
index 1a73c862d1..76bd9be889 100644
--- a/src/nvim/testdir/test32.in
+++ b/src/nvim/testdir/test32.in
@@ -21,7 +21,6 @@ Test for insert expansion
* t-expansion
STARTTEST
-:so small.vim
:se backspace=""
:se cpt=.,w ff=unix | $-2,$w!Xtestfile | set ff&
:se cot=
diff --git a/src/nvim/testdir/test34.in b/src/nvim/testdir/test34.in
index 71ee5f63b2..4cb7e9494a 100644
--- a/src/nvim/testdir/test34.in
+++ b/src/nvim/testdir/test34.in
@@ -4,7 +4,6 @@ Also test that a builtin function cannot be replaced.
Also test for regression when calling arbitrary expression.
STARTTEST
-:so small.vim
:function Table(title, ...)
: let ret = a:title
: let idx = 1
diff --git a/src/nvim/testdir/test36.in b/src/nvim/testdir/test36.in
deleted file mode 100644
index 8cdb5262bd..0000000000
--- a/src/nvim/testdir/test36.in
+++ /dev/null
@@ -1,105 +0,0 @@
-Test character classes in regexp using regexpengine 0, 1, 2.
-
-STARTTEST
-/^start-here/+1
-Y:s/\%#=0\d//g
-p:s/\%#=1\d//g
-p:s/\%#=2\d//g
-p:s/\%#=0[0-9]//g
-p:s/\%#=1[0-9]//g
-p:s/\%#=2[0-9]//g
-p:s/\%#=0\D//g
-p:s/\%#=1\D//g
-p:s/\%#=2\D//g
-p:s/\%#=0[^0-9]//g
-p:s/\%#=1[^0-9]//g
-p:s/\%#=2[^0-9]//g
-p:s/\%#=0\o//g
-p:s/\%#=1\o//g
-p:s/\%#=2\o//g
-p:s/\%#=0[0-7]//g
-p:s/\%#=1[0-7]//g
-p:s/\%#=2[0-7]//g
-p:s/\%#=0\O//g
-p:s/\%#=1\O//g
-p:s/\%#=2\O//g
-p:s/\%#=0[^0-7]//g
-p:s/\%#=1[^0-7]//g
-p:s/\%#=2[^0-7]//g
-p:s/\%#=0\x//g
-p:s/\%#=1\x//g
-p:s/\%#=2\x//g
-p:s/\%#=0[0-9A-Fa-f]//g
-p:s/\%#=1[0-9A-Fa-f]//g
-p:s/\%#=2[0-9A-Fa-f]//g
-p:s/\%#=0\X//g
-p:s/\%#=1\X//g
-p:s/\%#=2\X//g
-p:s/\%#=0[^0-9A-Fa-f]//g
-p:s/\%#=1[^0-9A-Fa-f]//g
-p:s/\%#=2[^0-9A-Fa-f]//g
-p:s/\%#=0\w//g
-p:s/\%#=1\w//g
-p:s/\%#=2\w//g
-p:s/\%#=0[0-9A-Za-z_]//g
-p:s/\%#=1[0-9A-Za-z_]//g
-p:s/\%#=2[0-9A-Za-z_]//g
-p:s/\%#=0\W//g
-p:s/\%#=1\W//g
-p:s/\%#=2\W//g
-p:s/\%#=0[^0-9A-Za-z_]//g
-p:s/\%#=1[^0-9A-Za-z_]//g
-p:s/\%#=2[^0-9A-Za-z_]//g
-p:s/\%#=0\h//g
-p:s/\%#=1\h//g
-p:s/\%#=2\h//g
-p:s/\%#=0[A-Za-z_]//g
-p:s/\%#=1[A-Za-z_]//g
-p:s/\%#=2[A-Za-z_]//g
-p:s/\%#=0\H//g
-p:s/\%#=1\H//g
-p:s/\%#=2\H//g
-p:s/\%#=0[^A-Za-z_]//g
-p:s/\%#=1[^A-Za-z_]//g
-p:s/\%#=2[^A-Za-z_]//g
-p:s/\%#=0\a//g
-p:s/\%#=1\a//g
-p:s/\%#=2\a//g
-p:s/\%#=0[A-Za-z]//g
-p:s/\%#=1[A-Za-z]//g
-p:s/\%#=2[A-Za-z]//g
-p:s/\%#=0\A//g
-p:s/\%#=1\A//g
-p:s/\%#=2\A//g
-p:s/\%#=0[^A-Za-z]//g
-p:s/\%#=1[^A-Za-z]//g
-p:s/\%#=2[^A-Za-z]//g
-p:s/\%#=0\l//g
-p:s/\%#=1\l//g
-p:s/\%#=2\l//g
-p:s/\%#=0[a-z]//g
-p:s/\%#=1[a-z]//g
-p:s/\%#=2[a-z]//g
-p:s/\%#=0\L//g
-p:s/\%#=1\L//g
-p:s/\%#=2\L//g
-p:s/\%#=0[^a-z]//g
-p:s/\%#=1[^a-z]//g
-p:s/\%#=2[^a-z]//g
-p:s/\%#=0\u//g
-p:s/\%#=1\u//g
-p:s/\%#=2\u//g
-p:s/\%#=0[A-Z]//g
-p:s/\%#=1[A-Z]//g
-p:s/\%#=2[A-Z]//g
-p:s/\%#=0\U//g
-p:s/\%#=1\U//g
-p:s/\%#=2\U//g
-p:s/\%#=0[^A-Z]//g
-p:s/\%#=1[^A-Z]//g
-p:s/\%#=2[^A-Z]//g
-:/^start-here/+1,$wq! test.out
-ENDTEST
-
-start-here
- !"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
diff --git a/src/nvim/testdir/test36.ok b/src/nvim/testdir/test36.ok
deleted file mode 100644
index f72a74b2b7..0000000000
--- a/src/nvim/testdir/test36.ok
+++ /dev/null
@@ -1,96 +0,0 @@
- !"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
-0123456789
-0123456789
-0123456789
-0123456789
-0123456789
-0123456789
- !"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
-01234567
-01234567
-01234567
-01234567
-01234567
-01234567
- !"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~€‚›¦±¼ÇÓé
-0123456789ABCDEFabcdef
-0123456789ABCDEFabcdef
-0123456789ABCDEFabcdef
-0123456789ABCDEFabcdef
-0123456789ABCDEFabcdef
-0123456789ABCDEFabcdef
- !"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
-0123456789ABCDEFGHIXYZ_abcdefghiwxyz
-0123456789ABCDEFGHIXYZ_abcdefghiwxyz
-0123456789ABCDEFGHIXYZ_abcdefghiwxyz
-0123456789ABCDEFGHIXYZ_abcdefghiwxyz
-0123456789ABCDEFGHIXYZ_abcdefghiwxyz
-0123456789ABCDEFGHIXYZ_abcdefghiwxyz
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~€‚›¦±¼ÇÓé
-ABCDEFGHIXYZ_abcdefghiwxyz
-ABCDEFGHIXYZ_abcdefghiwxyz
-ABCDEFGHIXYZ_abcdefghiwxyz
-ABCDEFGHIXYZ_abcdefghiwxyz
-ABCDEFGHIXYZ_abcdefghiwxyz
-ABCDEFGHIXYZ_abcdefghiwxyz
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~€‚›¦±¼ÇÓé
-ABCDEFGHIXYZabcdefghiwxyz
-ABCDEFGHIXYZabcdefghiwxyz
-ABCDEFGHIXYZabcdefghiwxyz
-ABCDEFGHIXYZabcdefghiwxyz
-ABCDEFGHIXYZabcdefghiwxyz
-ABCDEFGHIXYZabcdefghiwxyz
- !"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~€‚›¦±¼ÇÓé
-abcdefghiwxyz
-abcdefghiwxyz
-abcdefghiwxyz
-abcdefghiwxyz
-abcdefghiwxyz
-abcdefghiwxyz
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
- !"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~€‚›¦±¼ÇÓé
-ABCDEFGHIXYZ
-ABCDEFGHIXYZ
-ABCDEFGHIXYZ
-ABCDEFGHIXYZ
-ABCDEFGHIXYZ
-ABCDEFGHIXYZ
diff --git a/src/nvim/testdir/test37.in b/src/nvim/testdir/test37.in
index 8ca1125793..156bf74a10 100644
--- a/src/nvim/testdir/test37.in
+++ b/src/nvim/testdir/test37.in
@@ -1,6 +1,6 @@
Test for 'scrollbind'. <eralston@computer.org> Do not add a line below!
STARTTEST
-:so small.vim
+:
:set noscrollbind
:set scrollopt=ver,jump
:set scrolloff=2
diff --git a/src/nvim/testdir/test40.in b/src/nvim/testdir/test40.in
index ced4572fb8..b0285709e5 100644
--- a/src/nvim/testdir/test40.in
+++ b/src/nvim/testdir/test40.in
@@ -1,7 +1,6 @@
Test for "*Cmd" autocommands
STARTTEST
-:so small.vim
:set wildchar=^E
:/^start/,$w! Xxx " write lines below to Xxx
:au BufReadCmd XtestA 0r Xxx|$del
diff --git a/src/nvim/testdir/test42.in b/src/nvim/testdir/test42.in
index c35569a76c..0ea0198d12 100644
--- a/src/nvim/testdir/test42.in
+++ b/src/nvim/testdir/test42.in
Binary files differ
diff --git a/src/nvim/testdir/test47.in b/src/nvim/testdir/test47.in
index 13ad82462f..c95c6a6850 100644
--- a/src/nvim/testdir/test47.in
+++ b/src/nvim/testdir/test47.in
@@ -1,7 +1,8 @@
Tests for vertical splits and filler lines in diff mode
+Also tests restoration of saved options by :diffoff.
+
STARTTEST
-:so small.vim
:" Disable the title to avoid xterm keeping the wrong one.
:set notitle noicon
/^1
@@ -10,8 +11,19 @@ pkdd:w! Xtest
ddGpkkrXoxxx:w! Xtest2
:file Nop
ggoyyyjjjozzzz
+:set foldmethod=marker foldcolumn=4
+:redir => nodiffsettings
+:silent! :set diff? fdm? fdc? scb? crb? wrap?
+:redir END
:vert diffsplit Xtest
:vert diffsplit Xtest2
+:redir => diffsettings
+:silent! :set diff? fdm? fdc? scb? crb? wrap?
+:redir END
+:let diff_fdm = &fdm
+:let diff_fdc = &fdc
+:" repeat entering diff mode here to see if this saves the wrong settings
+:diffthis
:" jump to second window for a moment to have filler line appear at start of
:" first window
ggpgg:let one = winline()
@@ -36,8 +48,36 @@ j:let three = three . "-" . winline()
:call append("$", two)
:call append("$", three)
:$-2,$w! test.out
-:" Test that diffing shows correct filler lines
+:"
+:" Test diffoff
:diffoff!
+1
+:let &diff = 1
+:let &fdm = diff_fdm
+:let &fdc = diff_fdc
+4
+:diffoff!
+:$put =nodiffsettings
+:$put =diffsettings
+1
+:redir => nd1
+:silent! :set diff? fdm? fdc? scb? crb? wrap?
+:redir END
+
+:redir => nd2
+:silent! :set diff? fdm? fdc? scb? crb? wrap?
+:redir END
+
+:redir => nd3
+:silent! :set diff? fdm? fdc? scb? crb? wrap?
+:redir END
+
+:$put =nd1
+:$put =nd2
+:$put =nd3
+:$-39,$w >> test.out
+:"
+:" Test that diffing shows correct filler lines
:windo :bw!
:enew
:put =range(4,10)
@@ -51,7 +91,7 @@ j:let three = three . "-" . winline()
:enew
:put =w0
:.w >> test.out
-:unlet! one two three w0
+:unlet! one two three nodiffsettings diffsettings diff_fdm diff_fdc nd1 nd2 nd3 w0
:qa!
ENDTEST
diff --git a/src/nvim/testdir/test47.ok b/src/nvim/testdir/test47.ok
index b1cba92b1c..83e96571ad 100644
--- a/src/nvim/testdir/test47.ok
+++ b/src/nvim/testdir/test47.ok
@@ -1,4 +1,44 @@
2-4-5-6-8-9
1-2-4-5-8
2-3-4-5-6-7-8
+
+
+nodiff
+ foldmethod=marker
+ foldcolumn=4
+noscrollbind
+nocursorbind
+ wrap
+
+
+ diff
+ foldmethod=diff
+ foldcolumn=2
+ scrollbind
+ cursorbind
+nowrap
+
+
+nodiff
+ foldmethod=marker
+ foldcolumn=4
+noscrollbind
+nocursorbind
+ wrap
+
+
+nodiff
+ foldmethod=marker
+ foldcolumn=4
+noscrollbind
+nocursorbind
+ wrap
+
+
+nodiff
+ foldmethod=marker
+ foldcolumn=4
+noscrollbind
+nocursorbind
+ wrap
1
diff --git a/src/nvim/testdir/test48.in b/src/nvim/testdir/test48.in
index 998e1bba00..1df5a3c46a 100644
--- a/src/nvim/testdir/test48.in
+++ b/src/nvim/testdir/test48.in
@@ -1,7 +1,6 @@
This is a test of 'virtualedit'.
STARTTEST
-:so small.vim
:set noswf
:set ve=all
j-dgg
diff --git a/src/nvim/testdir/test49.in b/src/nvim/testdir/test49.in
index 1ce57246ee..435e62765b 100644
--- a/src/nvim/testdir/test49.in
+++ b/src/nvim/testdir/test49.in
@@ -4,11 +4,12 @@ If after adding a new test, the test output doesn't appear properly in
test49.failed, try to add one or more "G"s at the line ending in "test.out"
STARTTEST
-:so small.vim
:se nomore
:lang mess C
:so test49.vim
-GGGGGGGGGGGGGG"rp:.-,$w! test.out
+:" Go back to this file and append the results from register r.
+:buf test49.in
+G"rp:/^Results/,$w! test.out
:"
:" make valgrind happy
:redir => funclist
diff --git a/src/nvim/testdir/test49.ok b/src/nvim/testdir/test49.ok
index bf1ceed9af..50fc5d2cef 100644
--- a/src/nvim/testdir/test49.ok
+++ b/src/nvim/testdir/test49.ok
@@ -1,19 +1,4 @@
Results of test49.vim:
-*** Test 1: OK (34695)
-*** Test 2: OK (34695)
-*** Test 3: OK (1384648195)
-*** Test 4: OK (32883)
-*** Test 5: OK (32883)
-*** Test 6: OK (603978947)
-*** Test 7: OK (90563)
-*** Test 8: OK (562493431)
-*** Test 9: OK (363)
-*** Test 10: OK (559615)
-*** Test 11: OK (2049)
-*** Test 12: OK (352256)
-*** Test 13: OK (145)
-*** Test 14: OK (42413)
-*** Test 15: OK (42413)
*** Test 16: OK (8722)
*** Test 17: OK (285127993)
*** Test 18: OK (67224583)
diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim
index afee9d882c..edd49a2b63 100644
--- a/src/nvim/testdir/test49.vim
+++ b/src/nvim/testdir/test49.vim
@@ -1,6 +1,6 @@
" Vim script language tests
" Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com>
-" Last Change: 2013 Jun 06
+" Last Change: 2016 Feb 07
"-------------------------------------------------------------------------------
" Test environment {{{1
@@ -608,850 +608,8 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>)
" END_OF_TEST_ENVIRONMENT - do not change or remove this line.
-"-------------------------------------------------------------------------------
-" Test 1: :endwhile in function {{{1
-"
-" Detect if a broken loop is (incorrectly) reactivated by the
-" :endwhile. Use a :return to prevent an endless loop, and make
-" this test first to get a meaningful result on an error before other
-" tests will hang.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! F()
- Xpath 1 " X: 1
- let first = 1
- XloopINIT 2 8
- while 1
- Xloop 1 " X: 2 + 0 * 16
- if first
- Xloop 2 " X: 4 + 0 * 32
- let first = 0
- XloopNEXT
- break
- else
- Xloop 4 " X: 0 + 0 * 64
- return
- endif
- endwhile
-endfunction
-
-call F()
-Xpath 128 " X: 128
-
-function! G()
- Xpath 256 " X: 256 + 0 * 2048
- let first = 1
- XloopINIT 512 8
- while 1
- Xloop 1 " X: 512 + 0 * 4096
- if first
- Xloop 2 " X: 1024 + 0 * 8192
- let first = 0
- XloopNEXT
- break
- else
- Xloop 4 " X: 0 + 0 * 16384
- return
- endif
- if 1 " unmatched :if
- endwhile
-endfunction
-
-call G()
-Xpath 32768 " X: 32768
-
-Xcheck 34695
-
-" Leave F and G for execution as scripts in the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 2: :endwhile in script {{{1
-"
-" Detect if a broken loop is (incorrectly) reactivated by the
-" :endwhile. Use a :finish to prevent an endless loop, and place
-" this test before others that might hang to get a meaningful result
-" on an error.
-"
-" This test executes the bodies of the functions F and G from the
-" previous test as script files (:return replaced by :finish).
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-ExecAsScript F " X: 1 + 2 + 4
-Xpath 128 " X: 128
-
-ExecAsScript G " X: 256 + 512 + 1024
-Xpath 32768 " X: 32768
-
-unlet first
-delfunction F
-delfunction G
-
-Xcheck 34695
-
-
-"-------------------------------------------------------------------------------
-" Test 3: :if, :elseif, :while, :continue, :break {{{1
-"-------------------------------------------------------------------------------
-
-XpathINIT
-if 1
- Xpath 1 " X: 1
- let loops = 3
- XloopINIT 2 512
- while loops > -1 " main loop: loops == 3, 2, 1 (which breaks)
- if loops <= 0
- let break_err = 1
- let loops = -1
- else " 3: 2: 1:
- Xloop 1 " X: 2 + 2*512 + 2*512*512
- endif
- if (loops == 2)
- while loops == 2 " dummy loop
- Xloop 2 " X: 4*512
- let loops = loops - 1
- continue " stop dummy loop
- Xloop 4 " X: 0
- endwhile
- XloopNEXT
- continue " continue main loop
- Xloop 8 " X: 0
- elseif (loops == 1)
- let p = 1
- while p " dummy loop
- Xloop 16 " X: 32*512*512
- let p = 0
- break " break dummy loop
- Xloop 32 " X: 0
- endwhile
- Xloop 64 " X: 128*512*512
- unlet p
- break " break main loop
- Xloop 128 " X: 0
- endif
- if (loops > 0)
- Xloop 256 " X: 512
- endif
- while loops == 3 " dummy loop
- let loops = loops - 1
- endwhile " end dummy loop
- XloopNEXT
- endwhile " end main loop
- Xpath 268435456 " X: 1024*512*512
-else
- Xpath 536870912 " X: 0
-endif
-Xpath 1073741824 " X: 4096*512*512
-if exists("break_err")
- " The Xpath command does not accept 2^31 (negative); add explicitly:
- let Xpath = Xpath + 2147483648 " X: 0
- unlet break_err
-endif
-
-unlet loops
-
-Xcheck 1384648195
-
-
-"-------------------------------------------------------------------------------
-" Test 4: :return {{{1
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! F()
- if 1
- Xpath 1 " X: 1
- let loops = 3
- XloopINIT 2 16
- while loops > 0 " 3: 2: 1:
- Xloop 1 " X: 2 + 2*16 + 0*16*16
- if (loops == 2)
- Xloop 2 " X: 4*16
- return
- Xloop 4 " X: 0
- endif
- Xloop 8 " X: 16
- let loops = loops - 1
- XloopNEXT
- endwhile
- Xpath 8192 " X: 0
- else
- Xpath 16384 " X: 0
- endif
-endfunction
-
-call F()
-Xpath 32768 " X: 8*16*16*16
-
-Xcheck 32883
-
-" Leave F for execution as a script in the next test.
-
-
-"-------------------------------------------------------------------------------
-" Test 5: :finish {{{1
-"
-" This test executes the body of the function F from the previous test
-" as a script file (:return replaced by :finish).
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-ExecAsScript F " X: 1 + 2 + 2*16 + 4*16 + 16
-Xpath 32768 " X: 32768
-
-unlet loops
-delfunction F
-
-Xcheck 32883
-
-
-"-------------------------------------------------------------------------------
-" Test 6: Defining functions in :while loops {{{1
-"
-" Functions can be defined inside other functions. An inner function
-" gets defined when the outer function is executed. Functions may
-" also be defined inside while loops. Expressions in braces for
-" defining the function name are allowed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if ExtraVim()
-
- " The command CALL collects the argument of all its invocations in "calls"
- " when used from a function (that is, when the global variable "calls" needs
- " the "g:" prefix). This is to check that the function code is skipped when
- " the function is defined. For inner functions, do so only if the outer
- " function is not being executed.
- "
- let calls = ""
- com! -nargs=1 CALL
- \ if !exists("calls") && !exists("outer") |
- \ let g:calls = g:calls . <args> |
- \ endif
-
-
- XloopINIT! 1 16
-
- let i = 0
- while i < 3
-
- XloopNEXT
- let i = i + 1
-
- if i == 1
- Xloop 1 " X: 1
- function! F1(arg)
- CALL a:arg
- let outer = 1
-
- XloopINIT! 4096 4
- let j = 0
- while j < 1
- XloopNEXT
- Xloop 1 " X: 4096
- let j = j + 1
- function! G1(arg)
- CALL a:arg
- endfunction
- Xloop 2 " X: 8192
- endwhile
- endfunction
- Xloop 2 " X: 2
-
- continue
- endif
-
- Xloop 4 " X: 4 * (16 + 256)
- function! F{i}(i, arg)
- CALL a:arg
- let outer = 1
-
- XloopINIT! 16384 4
- if a:i == 3
- XloopNEXT
- XloopNEXT
- XloopNEXT
- endif
- let k = 0
- while k < 3
- XloopNEXT
- Xloop 1 " X: 16384*(1+4+16+64+256+1024)
- let k = k + 1
- function! G{a:i}{k}(arg)
- CALL a:arg
- endfunction
- Xloop 2 " X: 32768*(1+4+16+64+256+1024)
- endwhile
- endfunction
- Xloop 8 " X: 8 * (16 + 256)
-
- endwhile
-
- if exists("*G1")
- Xpath 67108864 " X: 0
- endif
- if exists("*F1")
- call F1("F1")
- if exists("*G1")
- call G1("G1")
- endif
- endif
-
- if exists("G21") || exists("G21") || exists("G21")
- Xpath 134217728 " X: 0
- endif
- if exists("*F2")
- call F2(2, "F2")
- if exists("*G21")
- call G21("G21")
- endif
- if exists("*G22")
- call G22("G22")
- endif
- if exists("*G23")
- call G23("G23")
- endif
- endif
-
- if exists("G31") || exists("G31") || exists("G31")
- Xpath 268435456 " X: 0
- endif
- if exists("*F3")
- call F3(3, "F3")
- if exists("*G31")
- call G31("G31")
- endif
- if exists("*G32")
- call G32("G32")
- endif
- if exists("*G33")
- call G33("G33")
- endif
- endif
-
- Xpath 536870912 " X: 536870912
-
- if calls != "F1G1F2G21G22G23F3G31G32G33"
- Xpath 1073741824 " X: 0
- Xout "calls is" calls
- endif
-
- delfunction F1
- delfunction G1
- delfunction F2
- delfunction G21
- delfunction G22
- delfunction G23
- delfunction G31
- delfunction G32
- delfunction G33
-
-endif
-
-Xcheck 603978947
-
-
-"-------------------------------------------------------------------------------
-" Test 7: Continuing on errors outside functions {{{1
-"
-" On an error outside a function, the script processing continues
-" at the line following the outermost :endif or :endwhile. When not
-" inside an :if or :while, the script processing continues at the next
-" line.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-if 1
- Xpath 1 " X: 1
- while 1
- Xpath 2 " X: 2
- asdf
- Xpath 4 " X: 0
- break
- endwhile | Xpath 8 " X: 0
- Xpath 16 " X: 0
-endif | Xpath 32 " X: 0
-Xpath 64 " X: 64
-
-while 1
- Xpath 128 " X: 128
- if 1
- Xpath 256 " X: 256
- asdf
- Xpath 512 " X: 0
- endif | Xpath 1024 " X: 0
- Xpath 2048 " X: 0
- break
-endwhile | Xpath 4096 " X: 0
-Xpath 8192 " X: 8192
-
-asdf
-Xpath 16384 " X: 16384
-
-asdf | Xpath 32768 " X: 0
-Xpath 65536 " X: 65536
-
-Xcheck 90563
-
-
-"-------------------------------------------------------------------------------
-" Test 8: Aborting and continuing on errors inside functions {{{1
-"
-" On an error inside a function without the "abort" attribute, the
-" script processing continues at the next line (unless the error was
-" in a :return command). On an error inside a function with the
-" "abort" attribute, the function is aborted and the script processing
-" continues after the function call; the value -1 is returned then.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! F()
- if 1
- Xpath 1 " X: 1
- while 1
- Xpath 2 " X: 2
- asdf
- Xpath 4 " X: 4
- asdf | Xpath 8 " X: 0
- Xpath 16 " X: 16
- break
- endwhile
- Xpath 32 " X: 32
- endif | Xpath 64 " X: 64
- Xpath 128 " X: 128
-
- while 1
- Xpath 256 " X: 256
- if 1
- Xpath 512 " X: 512
- asdf
- Xpath 1024 " X: 1024
- asdf | Xpath 2048 " X: 0
- Xpath 4096 " X: 4096
- endif
- Xpath 8192 " X: 8192
- break
- endwhile | Xpath 16384 " X: 16384
- Xpath 32768 " X: 32768
-
- return novar " returns (default return value 0)
- Xpath 65536 " X: 0
- return 1 " not reached
-endfunction
-
-function! G() abort
- if 1
- Xpath 131072 " X: 131072
- while 1
- Xpath 262144 " X: 262144
- asdf " returns -1
- Xpath 524288 " X: 0
- break
- endwhile
- Xpath 1048576 " X: 0
- endif | Xpath 2097152 " X: 0
- Xpath Xpath 4194304 " X: 0
-
- return -4 " not reached
-endfunction
-
-function! H() abort
- while 1
- Xpath 8388608 " X: 8388608
- if 1
- Xpath 16777216 " X: 16777216
- asdf " returns -1
- Xpath 33554432 " X: 0
- endif
- Xpath 67108864 " X: 0
- break
- endwhile | Xpath 134217728 " X: 0
- Xpath 268435456 " X: 0
-
- return -4 " not reached
-endfunction
-
-" Aborted functions (G and H) return -1.
-let sum = (F() + 1) - 4*G() - 8*H()
-Xpath 536870912 " X: 536870912
-if sum != 13
- Xpath 1073741824 " X: 0
- Xout "sum is" sum
-endif
-
-unlet sum
-delfunction F
-delfunction G
-delfunction H
-
-Xcheck 562493431
-
-
-"-------------------------------------------------------------------------------
-" Test 9: Continuing after aborted functions {{{1
-"
-" When a function with the "abort" attribute is aborted due to an
-" error, the next function back in the call hierarchy without an
-" "abort" attribute continues; the value -1 is returned then.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! F() abort
- Xpath 1 " X: 1
- let result = G() " not aborted
- Xpath 2 " X: 2
- if result != 2
- Xpath 4 " X: 0
- endif
- return 1
-endfunction
-
-function! G() " no abort attribute
- Xpath 8 " X: 8
- if H() != -1 " aborted
- Xpath 16 " X: 0
- endif
- Xpath 32 " X: 32
- return 2
-endfunction
-
-function! H() abort
- Xpath 64 " X: 64
- call I() " aborted
- Xpath 128 " X: 0
- return 4
-endfunction
-
-function! I() abort
- Xpath 256 " X: 256
- asdf " error
- Xpath 512 " X: 0
- return 8
-endfunction
-
-if F() != 1
- Xpath 1024 " X: 0
-endif
-
-delfunction F
-delfunction G
-delfunction H
-delfunction I
-
-Xcheck 363
-
-
-"-------------------------------------------------------------------------------
-" Test 10: :if, :elseif, :while argument parsing {{{1
-"
-" A '"' or '|' in an argument expression must not be mixed up with
-" a comment or a next command after a bar. Parsing errors should
-" be recognized.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-function! MSG(enr, emsg)
- let english = v:lang == "C" || v:lang =~ '^[Ee]n'
- if a:enr == ""
- Xout "TODO: Add message number for:" a:emsg
- let v:errmsg = ":" . v:errmsg
- endif
- let match = 1
- if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
- let match = 0
- if v:errmsg == ""
- Xout "Message missing."
- else
- let v:errmsg = escape(v:errmsg, '"')
- Xout "Unexpected message:" v:errmsg
- endif
- endif
- return match
-endfunction
-
-if 1 || strlen("\"") | Xpath 1 " X: 1
- Xpath 2 " X: 2
-endif
-Xpath 4 " X: 4
-
-if 0
-elseif 1 || strlen("\"") | Xpath 8 " X: 8
- Xpath 16 " X: 16
-endif
-Xpath 32 " X: 32
-
-while 1 || strlen("\"") | Xpath 64 " X: 64
- Xpath 128 " X: 128
- break
-endwhile
-Xpath 256 " X: 256
-
-let v:errmsg = ""
-if 1 ||| strlen("\"") | Xpath 512 " X: 0
- Xpath 1024 " X: 0
-endif
-Xpath 2048 " X: 2048
-if !MSG('E15', "Invalid expression")
- Xpath 4096 " X: 0
-endif
-
-let v:errmsg = ""
-if 0
-elseif 1 ||| strlen("\"") | Xpath 8192 " X: 0
- Xpath 16384 " X: 0
-endif
-Xpath 32768 " X: 32768
-if !MSG('E15', "Invalid expression")
- Xpath 65536 " X: 0
-endif
-
-let v:errmsg = ""
-while 1 ||| strlen("\"") | Xpath 131072 " X: 0
- Xpath 262144 " X: 0
- break
-endwhile
-Xpath 524288 " X: 524288
-if !MSG('E15', "Invalid expression")
- Xpath 1048576 " X: 0
-endif
-
-delfunction MSG
-
-Xcheck 559615
-
-
-"-------------------------------------------------------------------------------
-" Test 11: :if, :elseif, :while argument evaluation after abort {{{1
-"
-" When code is skipped over due to an error, the boolean argument to
-" an :if, :elseif, or :while must not be evaluated.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-let calls = 0
-
-function! P(num)
- let g:calls = g:calls + a:num " side effect on call
- return 0
-endfunction
-
-if 1
- Xpath 1 " X: 1
- asdf " error
- Xpath 2 " X: 0
- if P(1) " should not be called
- Xpath 4 " X: 0
- elseif !P(2) " should not be called
- Xpath 8 " X: 0
- else
- Xpath 16 " X: 0
- endif
- Xpath 32 " X: 0
- while P(4) " should not be called
- Xpath 64 " X: 0
- endwhile
- Xpath 128 " X: 0
-endif
-
-if calls % 2
- Xpath 256 " X: 0
-endif
-if (calls/2) % 2
- Xpath 512 " X: 0
-endif
-if (calls/4) % 2
- Xpath 1024 " X: 0
-endif
-Xpath 2048 " X: 2048
-
-unlet calls
-delfunction P
-
-Xcheck 2049
-
-
-"-------------------------------------------------------------------------------
-" Test 12: Expressions in braces in skipped code {{{1
-"
-" In code skipped over due to an error or inactive conditional,
-" an expression in braces as part of a variable or function name
-" should not be evaluated.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-XloopINIT 1 8
-
-function! NULL()
- Xloop 1 " X: 0
- return 0
-endfunction
-
-function! ZERO()
- Xloop 2 " X: 0
- return 0
-endfunction
-
-function! F0()
- Xloop 4 " X: 0
-endfunction
-
-function! F1(arg)
- Xpath 4096 " X: 0
-endfunction
-
-let V0 = 1
-
-Xpath 8192 " X: 8192
-echo 0 ? F{NULL() + V{ZERO()}}() : 1
-XloopNEXT
-
-Xpath 16384 " X: 16384
-if 0
- Xpath 32768 " X: 0
- call F{NULL() + V{ZERO()}}()
-endif
-XloopNEXT
-
-Xpath 65536 " X: 65536
-if 1
- asdf " error
- Xpath 131072 " X: 0
- call F1(F{NULL() + V{ZERO()}}())
-endif
-XloopNEXT
-
-Xpath 262144 " X: 262144
-if 1
- asdf " error
- Xpath 524288 " X: 0
- call F{NULL() + V{ZERO()}}()
-endif
-
-Xcheck 352256
-
-
-"-------------------------------------------------------------------------------
-" Test 13: Failure in argument evaluation for :while {{{1
-"
-" A failure in the expression evaluation for the condition of a :while
-" causes the whole :while loop until the matching :endwhile being
-" ignored. Continuation is at the next following line.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-
-Xpath 1 " X: 1
-while asdf
- Xpath 2 " X: 0
- while 1
- Xpath 4 " X: 0
- break
- endwhile
- Xpath 8 " X: 0
- break
-endwhile
-Xpath 16 " X: 16
-
-while asdf | Xpath 32 | endwhile | Xpath 64 " X: 0
-Xpath 128 " X: 128
-
-Xcheck 145
-
-
-"-------------------------------------------------------------------------------
-" Test 14: Failure in argument evaluation for :if {{{1
-"
-" A failure in the expression evaluation for the condition of an :if
-" does not cause the corresponding :else or :endif being matched to
-" a previous :if/:elseif. Neither of both branches of the failed :if
-" are executed.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-XloopINIT 1 256
-
-function! F()
- Xloop 1 " X: 1 + 256 * 1
- let x = 0
- if x " false
- Xloop 2 " X: 0 + 256 * 0
- elseif !x " always true
- Xloop 4 " X: 4 + 256 * 4
- let x = 1
- if g:boolvar " possibly undefined
- Xloop 8 " X: 8 + 256 * 0
- else
- Xloop 16 " X: 0 + 256 * 0
- endif
- Xloop 32 " X: 32 + 256 * 32
- elseif x " never executed
- Xloop 64 " X: 0 + 256 * 0
- endif
- Xloop 128 " X: 128 + 256 * 128
-endfunction
-
-let boolvar = 1
-call F()
-
-XloopNEXT
-unlet boolvar
-call F()
-
-delfunction F
-
-Xcheck 42413
-
-
-"-------------------------------------------------------------------------------
-" Test 15: Failure in argument evaluation for :if (bar) {{{1
-"
-" Like previous test, except that the failing :if ... | ... | :endif
-" is in a single line.
-"-------------------------------------------------------------------------------
-
-XpathINIT
-XloopINIT 1 256
-
-function! F()
- Xloop 1 " X: 1 + 256 * 1
- let x = 0
- if x " false
- Xloop 2 " X: 0 + 256 * 0
- elseif !x " always true
- Xloop 4 " X: 4 + 256 * 4
- let x = 1
- if g:boolvar | Xloop 8 | else | Xloop 16 | endif " X: 8
- Xloop 32 " X: 32 + 256 * 32
- elseif x " never executed
- Xloop 64 " X: 0 + 256 * 0
- endif
- Xloop 128 " X: 128 + 256 * 128
-endfunction
-
-let boolvar = 1
-call F()
-
-XloopNEXT
-unlet boolvar
-call F()
-
-delfunction F
-
-Xcheck 42413
-
+" Tests 1 to 15 were moved to test_viml.vim
+let Xtest = 16
"-------------------------------------------------------------------------------
" Test 16: Double :else or :elseif after :else {{{1
@@ -5188,19 +4346,19 @@ catch /.*/
Xpath 65536 " X: 65536
let exception = v:exception
let throwpoint = v:throwpoint
- call CHECK(1, "oops", '\<F\.\.G\.\.T\>', '\<2\>')
+ call CHECK(1, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
exec "let exception = v:exception"
exec "let throwpoint = v:throwpoint"
- call CHECK(2, "oops", '\<F\.\.G\.\.T\>', '\<2\>')
+ call CHECK(2, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
CmdException
CmdThrowpoint
- call CHECK(3, "oops", '\<F\.\.G\.\.T\>', '\<2\>')
+ call CHECK(3, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
call FuncException()
call FuncThrowpoint()
- call CHECK(4, "oops", '\<F\.\.G\.\.T\>', '\<2\>')
+ call CHECK(4, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
exec "source" scriptException
exec "source" scriptThrowPoint
- call CHECK(5, "oops", '\<F\.\.G\.\.T\>', '\<2\>')
+ call CHECK(5, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
try
Xpath 131072 " X: 131072
call G("arrgh", 4)
@@ -5208,7 +4366,7 @@ catch /.*/
Xpath 262144 " X: 262144
let exception = v:exception
let throwpoint = v:throwpoint
- call CHECK(6, "arrgh", '\<G\.\.T\>', '\<4\>')
+ call CHECK(6, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
try
Xpath 524288 " X: 524288
let g:arg = "autsch"
@@ -5226,7 +4384,7 @@ catch /.*/
Xpath 2097152 " X: 2097152
let exception = v:exception
let throwpoint = v:throwpoint
- call CHECK(8, "arrgh", '\<G\.\.T\>', '\<4\>')
+ call CHECK(8, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
try
Xpath 4194304 " X: 4194304
let g:arg = "brrrr"
@@ -5242,27 +4400,27 @@ catch /.*/
Xpath 16777216 " X: 16777216
let exception = v:exception
let throwpoint = v:throwpoint
- call CHECK(10, "arrgh", '\<G\.\.T\>', '\<4\>')
+ call CHECK(10, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
endtry
Xpath 33554432 " X: 33554432
let exception = v:exception
let throwpoint = v:throwpoint
- call CHECK(11, "arrgh", '\<G\.\.T\>', '\<4\>')
+ call CHECK(11, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
endtry
Xpath 67108864 " X: 67108864
let exception = v:exception
let throwpoint = v:throwpoint
- call CHECK(12, "arrgh", '\<G\.\.T\>', '\<4\>')
+ call CHECK(12, "arrgh", '\<G\[1]\.\.T\>', '\<4\>')
finally
Xpath 134217728 " X: 134217728
let exception = v:exception
let throwpoint = v:throwpoint
- call CHECK(13, "oops", '\<F\.\.G\.\.T\>', '\<2\>')
+ call CHECK(13, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
endtry
Xpath 268435456 " X: 268435456
let exception = v:exception
let throwpoint = v:throwpoint
- call CHECK(14, "oops", '\<F\.\.G\.\.T\>', '\<2\>')
+ call CHECK(14, "oops", '\<F\[1]\.\.G\[1]\.\.T\>', '\<2\>')
finally
Xpath 536870912 " X: 536870912
let exception = v:exception
@@ -6591,8 +5749,7 @@ function! F()
if !caught && !$VIMNOERRTHROW
Xpath 8192 " X: 0
endif
- if caught ? !MSG('E55', 'Unmatched \\)')
- \ : !MSG('E475', "Invalid argument")
+ if !MSG('E475', "Invalid argument")
Xpath 16384 " X: 0
endif
if !caught
diff --git a/src/nvim/testdir/test50.in b/src/nvim/testdir/test50.in
index 0cbf4bf6d6..392177b808 100644
--- a/src/nvim/testdir/test50.in
+++ b/src/nvim/testdir/test50.in
@@ -2,7 +2,6 @@ Test for shortpathname ':8' extension.
Only for use on Win32 systems!
STARTTEST
-:so small.vim
:fun! TestIt(file, bits, expected)
let res=fnamemodify(a:file,a:bits)
if a:expected == ''
diff --git a/src/nvim/testdir/test52.in b/src/nvim/testdir/test52.in
index 206b65a1f9..fa75129193 100644
--- a/src/nvim/testdir/test52.in
+++ b/src/nvim/testdir/test52.in
@@ -1,7 +1,6 @@
Tests for reading and writing files with conversion for Win32.
STARTTEST
-:so mbyte.vim
:" make this a dummy test for non-Win32 systems
:if !has("win32") | e! test.ok | wq! test.out | endif
:"
diff --git a/src/nvim/testdir/test53.in b/src/nvim/testdir/test53.in
index 7c35b2e853..f3778c5192 100644
--- a/src/nvim/testdir/test53.in
+++ b/src/nvim/testdir/test53.in
@@ -7,7 +7,6 @@ Also test match() and matchstr()
Also test the gn command and repeating it.
STARTTEST
-:so small.vim
/^start:/
da"
0va'a'rx
diff --git a/src/nvim/testdir/test55.in b/src/nvim/testdir/test55.in
index 7b6f684caa..9a55eac6f6 100644
--- a/src/nvim/testdir/test55.in
+++ b/src/nvim/testdir/test55.in
@@ -1,7 +1,6 @@
Tests for List and Dictionary types. vim: set ft=vim :
STARTTEST
-:so small.vim
:fun Test(...)
:lang C
:" Creating List directly with different types
@@ -442,6 +441,17 @@ let l = [0, 1, 2, 3]
:unlockvar 1 b:
:unlet! b:testvar
:"
+:$put ='No :let += of locked list variable:'
+:let l = ['a', 'b', 3]
+:lockvar 1 l
+:try
+: let l += ['x']
+: $put ='did :let +='
+:catch
+: $put =v:exception[:14]
+:endtry
+:$put =string(l)
+:"
:unlet l
:let l = [1, 2, 3, 4]
:lockvar! l
diff --git a/src/nvim/testdir/test55.ok b/src/nvim/testdir/test55.ok
index 4e0303c26e..607a95ead9 100644
--- a/src/nvim/testdir/test55.ok
+++ b/src/nvim/testdir/test55.ok
@@ -144,6 +144,9 @@ No extend() of write-protected scope-level variable:
Vim(put):E742:
No :unlet of variable in locked scope:
Vim(unlet):E741:
+No :let += of locked list variable:
+Vim(let):E741:
+['a', 'b', 3]
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4]
diff --git a/src/nvim/testdir/test64.in b/src/nvim/testdir/test64.in
index fd19d3af32..c4585ecbce 100644
--- a/src/nvim/testdir/test64.in
+++ b/src/nvim/testdir/test64.in
@@ -5,7 +5,6 @@ A pattern that gives the expected result produces OK, so that we know it was
actually tried.
STARTTEST
-:so small.vim
:" tl is a List of Lists with:
:" regexp engine
:" regexp pattern
diff --git a/src/nvim/testdir/test68.in b/src/nvim/testdir/test68.in
deleted file mode 100644
index ca54e942b5..0000000000
--- a/src/nvim/testdir/test68.in
+++ /dev/null
@@ -1,131 +0,0 @@
-Test for text formatting.
-
-Results of test68:
-
-STARTTEST
-:so small.vim
-/^{/+1
-:set noai tw=2 fo=t
-gRa b
-ENDTEST
-
-{
-
-
-}
-
-STARTTEST
-/^{/+1
-:set ai tw=2 fo=tw
-gqgqjjllab
-ENDTEST
-
-{
-a b
-
-a
-}
-
-STARTTEST
-/^{/+1
-:set tw=3 fo=t
-gqgqo
-a 
-ENDTEST
-
-{
-a 
-}
-
-STARTTEST
-/^{/+1
-:set tw=2 fo=tcq1 comments=:#
-gqgqjgqgqo
-a b
-#a b
-ENDTEST
-
-{
-a b
-#a b
-}
-
-STARTTEST
-/^{/+1
-:set tw=5 fo=tcn comments=:#
-A bjA b
-ENDTEST
-
-{
- 1 a
-# 1 a
-}
-
-STARTTEST
-/^{/+3
-:set tw=5 fo=t2a si
-i A_
-ENDTEST
-
-{
-
- x a
- b
- c
-
-}
-
-STARTTEST
-/^{/+1
-:set tw=5 fo=qn comments=:#
-gwap
-ENDTEST
-
-{
-# 1 a b
-}
-
-STARTTEST
-/^{/+1
-:set tw=5 fo=q2 comments=:#
-gwap
-ENDTEST
-
-{
-# x
-# a b
-}
-
-STARTTEST
-/^{/+2
-:set tw& fo=a
-I^^
-ENDTEST
-
-{
- 1aa
- 2bb
-}
-
-STARTTEST
-/mno pqr/
-:setl tw=20 fo=an12wcq comments=s1:/*,mb:*,ex:*/
-A vwx yz
-ENDTEST
-
-/* abc def ghi jkl
- * mno pqr stu
- */
-
-STARTTEST
-/^#/
-:setl tw=12 fo=tqnc comments=:#
-A foobar
-ENDTEST
-
-# 1 xxxxx
-
-STARTTEST
-:g/^STARTTEST/.,/^ENDTEST/d
-:1;/^Results/,$wq! test.out
-ENDTEST
diff --git a/src/nvim/testdir/test68.ok b/src/nvim/testdir/test68.ok
deleted file mode 100644
index b3726a0a27..0000000000
--- a/src/nvim/testdir/test68.ok
+++ /dev/null
@@ -1,77 +0,0 @@
-Results of test68:
-
-
-{
-a
-b
-}
-
-
-{
-a
-b
-
-a
-b
-}
-
-
-{
-a
-
-
-a
-
-}
-
-
-{
-a b
-#a b
-
-a b
-#a b
-}
-
-
-{
- 1 a
- b
-# 1 a
-# b
-}
-
-
-{
-
- x a
- b_
- c
-
-}
-
-
-{
-# 1 a
-# b
-}
-
-
-{
-# x a
-# b
-}
-
-
-{ 1aa ^^2bb }
-
-
-/* abc def ghi jkl
- * mno pqr stu
- * vwx yz
- */
-
-
-# 1 xxxxx
-# foobar
-
diff --git a/src/nvim/testdir/test69.in b/src/nvim/testdir/test69.in
index f583947dfb..39b360fc81 100644
--- a/src/nvim/testdir/test69.in
+++ b/src/nvim/testdir/test69.in
@@ -4,7 +4,7 @@ And test "ra" on multi-byte characters.
Also test byteidx() and byteidxcomp()
STARTTEST
-:so mbyte.vim
+:
ENDTEST
Results of test69:
diff --git a/src/nvim/testdir/test73.in b/src/nvim/testdir/test73.in
index c525e51d28..7d6c7287a5 100644
--- a/src/nvim/testdir/test73.in
+++ b/src/nvim/testdir/test73.in
@@ -1,7 +1,6 @@
Tests for find completion.
STARTTEST
-:so small.vim
:set wildmode=full
:" Do all test in a separate window to avoid E211 when we recursively
:" delete the Xfind directory during cleanup
diff --git a/src/nvim/testdir/test8.in b/src/nvim/testdir/test8.in
index 41e6262e92..a5e6034782 100644
--- a/src/nvim/testdir/test8.in
+++ b/src/nvim/testdir/test8.in
@@ -2,7 +2,6 @@ Test for BufWritePre autocommand that deletes or unloads the buffer.
Test for BufUnload autocommand that unloads all other buffers.
STARTTEST
-:so small.vim
:au BufWritePre Xxx1 bunload
:au BufWritePre Xxx2 bwipe
/^start of
@@ -35,8 +34,6 @@ endfunc
:set shada='100
:au BufUnload * call CloseAll()
:au VimLeave * call WriteToOut()
-:e small.vim
-:sp mbyte.vim
:q
:qa!
ENDTEST
diff --git a/src/nvim/testdir/test88.in b/src/nvim/testdir/test88.in
deleted file mode 100644
index 9e43f703e9..0000000000
--- a/src/nvim/testdir/test88.in
+++ /dev/null
@@ -1,99 +0,0 @@
-vim: set ft=vim
-
-Tests for correct display (cursor column position) with +conceal and
-tabulators.
-
-STARTTEST
-:so small.vim
-:if !has('conceal')
- e! test.ok
- wq! test.out
-:endif
-:" Conceal settings.
-:set conceallevel=2
-:set concealcursor=nc
-:syntax match test /|/ conceal
-:" Save current cursor position. Only works in <expr> mode, can't be used
-:" with :normal because it moves the cursor to the command line. Thanks to ZyX
-:" <zyx.vim@gmail.com> for the idea to use an <expr> mapping.
-:let positions = []
-:nnoremap <expr> GG ":let positions += ['".screenrow().":".screencol()."']\n"
-:" Start test.
-/^start:
-:normal ztj
-GGk
-:" We should end up in the same column when running these commands on the two
-:" lines.
-:normal ft
-GGk
-:normal $
-GGk
-:normal 0j
-GGk
-:normal ft
-GGk
-:normal $
-GGk
-:normal 0j0j
-GGk
-:" Same for next test block.
-:normal ft
-GGk
-:normal $
-GGk
-:normal 0j
-GGk
-:normal ft
-GGk
-:normal $
-GGk
-:normal 0j0j
-GGk
-:" And check W with multiple tabs and conceals in a line.
-:normal W
-GGk
-:normal W
-GGk
-:normal W
-GGk
-:normal $
-GGk
-:normal 0j
-GGk
-:normal W
-GGk
-:normal W
-GGk
-:normal W
-GGk
-:normal $
-GGk
-:set lbr
-:normal $
-GGk
-:set list listchars=tab:>-
-:normal 0
-GGk
-:normal W
-GGk
-:normal W
-GGk
-:normal W
-GGk
-:normal $
-GGk
-:" Display result.
-:call append('$', 'end:')
-:call append('$', positions)
-:/^end/,$wq! test.out
-ENDTEST
-
-start:
-.concealed. text
-|concealed| text
-
- .concealed. text
- |concealed| text
-
-.a. .b. .c. .d.
-|a| |b| |c| |d|
diff --git a/src/nvim/testdir/test88.ok b/src/nvim/testdir/test88.ok
deleted file mode 100644
index 12949f274a..0000000000
--- a/src/nvim/testdir/test88.ok
+++ /dev/null
@@ -1,29 +0,0 @@
-end:
-2:1
-2:17
-2:20
-3:1
-3:17
-3:20
-5:8
-5:25
-5:28
-6:8
-6:25
-6:28
-8:1
-8:9
-8:17
-8:25
-8:27
-9:1
-9:9
-9:17
-9:25
-9:26
-9:26
-9:1
-9:9
-9:17
-9:25
-9:26
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
new file mode 100644
index 0000000000..1d1da94bac
--- /dev/null
+++ b/src/nvim/testdir/test_alot.vim
@@ -0,0 +1,3 @@
+" A series of tests that can run in one Vim invocation.
+" This makes testing go faster, since Vim doesn't need to restart.
+
diff --git a/src/nvim/testdir/test_breakindent.in b/src/nvim/testdir/test_breakindent.in
deleted file mode 100644
index 5a8e580c4a..0000000000
--- a/src/nvim/testdir/test_breakindent.in
+++ /dev/null
@@ -1,123 +0,0 @@
-Test for breakindent
-
-STARTTEST
-:so small.vim
-:if !exists("+breakindent") | e! test.ok | w! test.out | qa! | endif
-:set wildchar=^E
-:10new|:vsp|:vert resize 20
-:put =\"\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP\"
-:set ts=4 sw=4 sts=4 breakindent
-:fu! ScreenChar(line, width)
-: let c=''
-: for i in range(1,a:width)
-: let c.=nr2char(screenchar(a:line, i))
-: endfor
-: let c.="\n"
-: for i in range(1,a:width)
-: let c.=nr2char(screenchar(a:line+1, i))
-: endfor
-: let c.="\n"
-: for i in range(1,a:width)
-: let c.=nr2char(screenchar(a:line+2, i))
-: endfor
-: return c
-:endfu
-:fu DoRecordScreen()
-: wincmd l
-: $put =printf(\"\n%s\", g:test)
-: $put =g:line1
-: wincmd p
-:endfu
-:set briopt=min:0
-:let g:test="Test 1: Simple breakindent"
-:let line1=ScreenChar(line('.'),8)
-:call DoRecordScreen()
-:let g:test="Test 2: Simple breakindent + sbr=>>"
-:set sbr=>>
-:let line1=ScreenChar(line('.'),8)
-:call DoRecordScreen()
-:let g:test ="Test 3: Simple breakindent + briopt:sbr"
-:set briopt=sbr,min:0 sbr=++
-:let line1=ScreenChar(line('.'),8)
-:call DoRecordScreen()
-:let g:test ="Test 4: Simple breakindent + min width: 18"
-:set sbr= briopt=min:18
-:let line1=ScreenChar(line('.'),8)
-:call DoRecordScreen()
-:let g:test =" Test 5: Simple breakindent + shift by 2"
-:set briopt=shift:2,min:0
-:let line1=ScreenChar(line('.'),8)
-:call DoRecordScreen()
-:let g:test=" Test 6: Simple breakindent + shift by -1"
-:set briopt=shift:-1,min:0
-:let line1=ScreenChar(line('.'),8)
-:call DoRecordScreen()
-:let g:test=" Test 7: breakindent + shift by +1 + nu + sbr=? briopt:sbr"
-:set briopt=shift:1,sbr,min:0 nu sbr=? nuw=4
-:let line1=ScreenChar(line('.'),10)
-:call DoRecordScreen()
-:let g:test=" Test 8: breakindent + shift:1 + nu + sbr=# list briopt:sbr"
-:set briopt=shift:1,sbr,min:0 nu sbr=# list lcs&vi
-:let line1=ScreenChar(line('.'),10)
-:call DoRecordScreen()
-:let g:test=" Test 9: breakindent + shift by +1 + 'nu' + sbr=# list"
-:set briopt-=sbr
-:let line1=ScreenChar(line('.'),10)
-:call DoRecordScreen()
-:let g:test=" Test 10: breakindent + shift by +1 + 'nu' + sbr=~ cpo+=n"
-:set cpo+=n sbr=~ nu nuw=4 nolist briopt=sbr,min:0
-:let line1=ScreenChar(line('.'),10)
-:call DoRecordScreen()
-:wincmd p
-:let g:test="\n Test 11: strdisplaywidth when breakindent is on"
-:set cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4
-:let text=getline(2) "skip leading tab when calculating text width
-:let width = strlen(text[1:])+indent(2)*4+strlen(&sbr)*3 " text wraps 3 times
-:$put =g:test
-:$put =printf(\"strdisplaywidth: %d == calculated: %d\", strdisplaywidth(text), width)
-:let g:str="\t\t\t\t\t{"
-:let g:test=" Test 12: breakindent + long indent"
-:wincmd p
-:set all& breakindent linebreak briopt=min:10 nu numberwidth=3 ts=4
-:$put =g:str
-zt:let line1=ScreenChar(1,10)
-:wincmd p
-:call DoRecordScreen()
-:"
-:" Test, that the string " a\tb\tc\td\te" is correctly
-:" displayed in a 20 column wide window (see bug report
-:" https://groups.google.com/d/msg/vim_dev/ZOdg2mc9c9Y/TT8EhFjEy0IJ
-:only
-:vert 20new
-:set all& breakindent briopt=min:10
-:call setline(1, [" a\tb\tc\td\te", " z y x w v"])
-:/^\s*a
-fbgjyl:let line1 = @0
-:?^\s*z
-fygjyl:let line2 = @0
-:quit!
-:$put ='Test 13: breakindent with wrapping Tab'
-:$put =line1
-:$put =line2
-:"
-:let g:test="Test 14: breakindent + visual blockwise delete #1"
-:set all& breakindent shada+=nX-test-breakindent.shada
-:30vnew
-:normal! 3a1234567890
-:normal! a abcde
-:exec "normal! 0\<C-V>tex"
-:let line1=ScreenChar(line('.'),8)
-:call DoRecordScreen()
-:"
-:let g:test="Test 15: breakindent + visual blockwise delete #2"
-:%d
-:normal! 4a1234567890
-:exec "normal! >>\<C-V>3f0x"
-:let line1=ScreenChar(line('.'),20)
-:call DoRecordScreen()
-:quit!
-:"
-:%w! test.out
-:qa!
-ENDTEST
-dummy text
diff --git a/src/nvim/testdir/test_breakindent.ok b/src/nvim/testdir/test_breakindent.ok
deleted file mode 100644
index 995bd5f29c..0000000000
--- a/src/nvim/testdir/test_breakindent.ok
+++ /dev/null
@@ -1,74 +0,0 @@
-
- abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP
-
-Test 1: Simple breakindent
- abcd
- qrst
- GHIJ
-
-Test 2: Simple breakindent + sbr=>>
- abcd
- >>qr
- >>EF
-
-Test 3: Simple breakindent + briopt:sbr
- abcd
-++ qrst
-++ GHIJ
-
-Test 4: Simple breakindent + min width: 18
- abcd
- qrstuv
- IJKLMN
-
- Test 5: Simple breakindent + shift by 2
- abcd
- qr
- EF
-
- Test 6: Simple breakindent + shift by -1
- abcd
- qrstu
- HIJKL
-
- Test 7: breakindent + shift by +1 + nu + sbr=? briopt:sbr
- 2 ab
- ? m
- ? x
-
- Test 8: breakindent + shift:1 + nu + sbr=# list briopt:sbr
- 2 ^Iabcd
- # opq
- # BCD
-
- Test 9: breakindent + shift by +1 + 'nu' + sbr=# list
- 2 ^Iabcd
- #op
- #AB
-
- Test 10: breakindent + shift by +1 + 'nu' + sbr=~ cpo+=n
- 2 ab
-~ mn
-~ yz
-
- Test 11: strdisplaywidth when breakindent is on
-strdisplaywidth: 46 == calculated: 64
- {
-
- Test 12: breakindent + long indent
-56
-
-~
-Test 13: breakindent with wrapping Tab
-d
-w
-
-Test 14: breakindent + visual blockwise delete #1
-e
-~
-~
-
-Test 15: breakindent + visual blockwise delete #2
- 1234567890
-~
-~
diff --git a/src/nvim/testdir/test_charsearch.in b/src/nvim/testdir/test_charsearch.in
deleted file mode 100644
index 5085cb39bc..0000000000
--- a/src/nvim/testdir/test_charsearch.in
+++ /dev/null
@@ -1,25 +0,0 @@
-Test for character searches
-
-STARTTEST
-:so small.vim
-:" check that "fe" and ";" work
-/^X
-ylfep;;p,,p:
-:" check that save/restore works
-/^Y
-ylfep:let csave = getcharsearch()
-fip:call setcharsearch(csave)
-;p;p:
-:" check that setcharsearch() changes the settins.
-/^Z
-ylfep:call setcharsearch({'char': 'k'})
-;p:call setcharsearch({'forward': 0})
-$;p:call setcharseearch({'until'}: 1})
-;;p:
-:/^X/,$w! test.out
-:qa!
-ENDTEST
-
-Xabcdefghijkemnopqretuvwxyz
-Yabcdefghijkemnopqretuvwxyz
-Zabcdefghijkemnokqretkvwxyz
diff --git a/src/nvim/testdir/test_charsearch.ok b/src/nvim/testdir/test_charsearch.ok
deleted file mode 100644
index a0c90e24f9..0000000000
--- a/src/nvim/testdir/test_charsearch.ok
+++ /dev/null
@@ -1,3 +0,0 @@
-XabcdeXfghijkeXmnopqreXtuvwxyz
-YabcdeYfghiYjkeYmnopqreYtuvwxyz
-ZabcdeZfghijkZemnokZqretkZvwxyz
diff --git a/src/nvim/testdir/test_close_count.in b/src/nvim/testdir/test_close_count.in
deleted file mode 100644
index 58dfb425ce..0000000000
--- a/src/nvim/testdir/test_close_count.in
+++ /dev/null
@@ -1,156 +0,0 @@
-Tests for :[count]close! and :[count]hide vim: set ft=vim :
-
-STARTTEST
-:let tests = []
-:so tiny.vim
-:for i in range(5)
-:new
-:endfor
-:4wincmd w
-:close!
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:1close!
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:$close!
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:1wincmd w
-:2close!
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:1wincmd w
-:new
-:new
-:2wincmd w
-:-1close!
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:2wincmd w
-:+1close!
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:e! test.out
-:call append(0, map(copy(tests), 'join(v:val, " ")'))
-:w
-:only!
-:b1
-ENDTEST
-
-STARTTEST
-:let tests = []
-:so tiny.vim
-:for i in range(5)
-:new
-:endfor
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:4wincmd w
-:.hide
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:1hide
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:$hide
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:1wincmd w
-:2hide
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:1wincmd w
-:new
-:new
-:3wincmd w
-:-hide
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:2wincmd w
-:+hide
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:e! test.out
-:call append(line('$'), map(copy(tests), 'join(v:val, " ")'))
-Go
-:w
-:only!
-:b1
-ENDTEST
-
-STARTTEST
-:let tests = []
-:so tiny.vim
-:set hidden
-:for i in range(5)
-:new
-:endfor
-:1wincmd w
-:$ hide
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:$-1 close!
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:1wincmd w
-:.+close!
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:e! test.out
-:call append(line('$'), map(copy(tests), 'join(v:val, " ")'))
-Go
-:w
-:only!
-:b1
-ENDTEST
-
-STARTTEST
-:let tests = []
-:so tiny.vim
-:set hidden
-:for i in range(5)
-:new
-:endfor
-:4wincmd w
-c
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-1c
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-9c
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:1wincmd w
-2c
-:let buffers = []
-:windo call add(buffers, bufnr('%'))
-:call add(tests, buffers)
-:only!
-:e! test.out
-:call append(line('$'), map(copy(tests), 'join(v:val, " ")'))
-:w
-:qa!
-ENDTEST
-
-
diff --git a/src/nvim/testdir/test_close_count.ok b/src/nvim/testdir/test_close_count.ok
deleted file mode 100644
index 1cee870487..0000000000
--- a/src/nvim/testdir/test_close_count.ok
+++ /dev/null
@@ -1,23 +0,0 @@
-6 5 4 2 1
-5 4 2 1
-5 4 2
-5 2
-7 5 2
-7 5
-
-13 12 11 10 9 1
-13 12 11 9 1
-12 11 9 1
-12 11 9
-12 9
-15 12 9
-15 12
-
-20 19 18 17 16
-20 19 18 16
-20 18 16
-
-25 24 23 21 1
-24 23 21 1
-24 23 21
-24 21
diff --git a/src/nvim/testdir/test_command_count.in b/src/nvim/testdir/test_command_count.in
deleted file mode 100644
index 170c810923..0000000000
--- a/src/nvim/testdir/test_command_count.in
+++ /dev/null
@@ -1,158 +0,0 @@
-Test for user command counts vim: set ft=vim :
-
-STARTTEST
-:so tiny.vim
-:lang C
-:let g:lines = []
-:com -range=% RangeLines :call add(g:lines, 'RangeLines '.<line1>.' '.<line2>)
-:com -range -addr=arguments RangeArguments :call add(g:lines, 'RangeArguments '.<line1>.' '.<line2>)
-:com -range=% -addr=arguments RangeArgumentsAll :call add(g:lines, 'RangeArgumentsAll '.<line1>.' '.<line2>)
-:com -range -addr=loaded_buffers RangeLoadedBuffers :call add(g:lines, 'RangeLoadedBuffers '.<line1>.' '.<line2>)
-:com -range=% -addr=loaded_buffers RangeLoadedBuffersAll :call add(g:lines, 'RangeLoadedBuffersAll '.<line1>.' '.<line2>)
-:com -range -addr=buffers RangeBuffers :call add(g:lines, 'RangeBuffers '.<line1>.' '.<line2>)
-:com -range=% -addr=buffers RangeBuffersAll :call add(g:lines, 'RangeBuffersAll '.<line1>.' '.<line2>)
-:com -range -addr=windows RangeWindows :call add(g:lines, 'RangeWindows '.<line1>.' '.<line2>)
-:com -range=% -addr=windows RangeWindowsAll :call add(g:lines, 'RangeWindowsAll '.<line1>.' '.<line2>)
-:com -range -addr=tabs RangeTabs :call add(g:lines, 'RangeTabs '.<line1>.' '.<line2>)
-:com -range=% -addr=tabs RangeTabsAll :call add(g:lines, 'RangeTabsAll '.<line1>.' '.<line2>)
-:set hidden
-:arga a b c d
-:argdo echo "loading buffers"
-:argu 3
-:.-,$-RangeArguments
-:%RangeArguments
-:RangeArgumentsAll
-:N
-:.RangeArguments
-:split|split|split|split
-:3wincmd w
-:.,$RangeWindows
-:%RangeWindows
-:RangeWindowsAll
-:only
-:blast|bd
-:.,$RangeLoadedBuffers
-:%RangeLoadedBuffers
-:RangeLoadedBuffersAll
-:.,$RangeBuffers
-:%RangeBuffers
-:RangeBuffersAll
-:tabe|tabe|tabe|tabe
-:normal 2gt
-:.,$RangeTabs
-:%RangeTabs
-:RangeTabsAll
-:1tabonly
-:s/\n/\r\r\r\r\r/
-:2ma<
-:$-ma>
-:'<,'>RangeLines
-:com -range=% -buffer LocalRangeLines :call add(g:lines, 'LocalRangeLines '.<line1>.' '.<line2>)
-:'<,'>LocalRangeLines
-:b1
-ENDTEST
-
-STARTTEST
-:call add(g:lines, '')
-:%argd
-:arga a b c d
-:let v:errmsg = ''
-:5argu
-:call add(g:lines, '5argu ' . v:errmsg)
-:$argu
-:call add(g:lines, '4argu ' . expand('%:t'))
-:let v:errmsg = ''
-:1argu
-:call add(g:lines, '1argu ' . expand('%:t'))
-:let v:errmsg = ''
-:100b
-:call add(g:lines, '100b ' . v:errmsg)
-:split|split|split|split
-:let v:errmsg = ''
-:0close
-:call add(g:lines, '0close ' . v:errmsg)
-:$wincmd w
-:$close
-:call add(g:lines, '$close ' . winnr())
-:let v:errmsg = ''
-:$+close
-:call add(g:lines, '$+close ' . v:errmsg)
-:$tabe
-:call add(g:lines, '$tabe ' . tabpagenr())
-:let v:errmsg = ''
-:$+tabe
-:call add(g:lines, '$+tabe ' . v:errmsg)
-:only!
-:e x
-:0tabm
-:normal 1gt
-:call add(g:lines, '0tabm ' . expand('%:t'))
-:tabonly!
-:only!
-:e! test.out
-:call append(0, g:lines)
-:unlet g:lines
-:w|bd
-:b1
-ENDTEST
-
-STARTTEST
-:let g:lines = []
-:func BufStatus()
-: call add(g:lines, 'aaa: ' . buflisted(g:buf_aaa) . ' bbb: ' . buflisted(g:buf_bbb) . ' ccc: ' . buflisted(g:buf_ccc))
-:endfunc
-:se nohidden
-:e aaa
-:let buf_aaa = bufnr('%')
-:e bbb
-:let buf_bbb = bufnr('%')
-:e ccc
-:let buf_ccc = bufnr('%')
-:b1
-:call BufStatus()
-:exe buf_bbb . "," . buf_ccc . "bdelete"
-:call BufStatus()
-:exe buf_aaa . "bdelete"
-:call BufStatus()
-:e! test.out
-:call append('$', g:lines)
-:unlet g:lines
-:delfunc BufStatus
-:w|bd
-:b1
-ENDTEST
-
-STARTTEST
-:se hidden
-:only!
-:let g:lines = []
-:%argd
-:arga a b c d e f
-:3argu
-:let args = ''
-:.,$-argdo let args .= ' '.expand('%')
-:call add(g:lines, 'argdo:' . args)
-:split|split|split|split
-:2wincmd w
-:let windows = ''
-:.,$-windo let windows .= ' '.winnr()
-:call add(g:lines, 'windo:'. windows)
-:b2
-:let buffers = ''
-:.,$-bufdo let buffers .= ' '.bufnr('%')
-:call add(g:lines, 'bufdo:' . buffers)
-:3bd
-:let buffers = ''
-:3,7bufdo let buffers .= ' '.bufnr('%')
-:call add(g:lines, 'bufdo:' . buffers)
-:tabe|tabe|tabe|tabe
-:normal! 2gt
-:let tabpages = ''
-:.,$-tabdo let tabpages .= ' '.tabpagenr()
-:call add(g:lines, 'tabdo:' . tabpages)
-:e! test.out
-:call append('$', g:lines)
-:w|qa!
-ENDTEST
-
-
diff --git a/src/nvim/testdir/test_command_count.ok b/src/nvim/testdir/test_command_count.ok
deleted file mode 100644
index e74155ec1b..0000000000
--- a/src/nvim/testdir/test_command_count.ok
+++ /dev/null
@@ -1,38 +0,0 @@
-RangeArguments 2 4
-RangeArguments 1 5
-RangeArgumentsAll 1 5
-RangeArguments 2 2
-RangeWindows 3 5
-RangeWindows 1 5
-RangeWindowsAll 1 5
-RangeLoadedBuffers 2 4
-RangeLoadedBuffers 1 4
-RangeLoadedBuffersAll 1 4
-RangeBuffers 2 5
-RangeBuffers 1 5
-RangeBuffersAll 1 5
-RangeTabs 2 5
-RangeTabs 1 5
-RangeTabsAll 1 5
-RangeLines 2 5
-LocalRangeLines 2 5
-
-5argu E16: Invalid range
-4argu d
-1argu a
-100b E16: Invalid range
-0close
-$close 3
-$+close E16: Invalid range
-$tabe 2
-$+tabe E16: Invalid range
-0tabm x
-
-aaa: 1 bbb: 1 ccc: 1
-aaa: 1 bbb: 0 ccc: 0
-aaa: 0 bbb: 0 ccc: 0
-argdo: c d e
-windo: 2 3 4
-bufdo: 2 3 4 5 6 7 8 9 10 15
-bufdo: 4 5 6 7
-tabdo: 2 3 4
diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim
new file mode 100644
index 0000000000..d819b7b092
--- /dev/null
+++ b/src/nvim/testdir/test_cursor_func.vim
@@ -0,0 +1,52 @@
+" Tests for cursor().
+
+func Test_wrong_arguments()
+ try
+ call cursor(1. 3)
+ " not reached
+ call assert_false(1)
+ catch
+ call assert_exception('E474:')
+ endtry
+endfunc
+
+func Test_move_cursor()
+ new
+ call setline(1, ['aaa', 'bbb', 'ccc', 'ddd'])
+
+ call cursor([1, 1, 0, 1])
+ call assert_equal([1, 1, 0, 1], getcurpos()[1:])
+ call cursor([4, 3, 0, 3])
+ call assert_equal([4, 3, 0, 3], getcurpos()[1:])
+
+ call cursor(2, 2)
+ call assert_equal([2, 2, 0, 2], getcurpos()[1:])
+ " line number zero keeps the line number
+ call cursor(0, 1)
+ call assert_equal([2, 1, 0, 1], getcurpos()[1:])
+ " col number zero keeps the column
+ call cursor(3, 0)
+ call assert_equal([3, 1, 0, 1], getcurpos()[1:])
+ " below last line goes to last line
+ call cursor(9, 1)
+ call assert_equal([4, 1, 0, 1], getcurpos()[1:])
+
+ quit!
+endfunc
+
+" Very short version of what matchparen does.
+function s:Highlight_Matching_Pair()
+ let save_cursor = getcurpos()
+ call setpos('.', save_cursor)
+endfunc
+
+func Test_curswant_with_autocommand()
+ new
+ call setline(1, ['func()', '{', '}', '----'])
+ autocmd! CursorMovedI * call s:Highlight_Matching_Pair()
+ exe "normal! 3Ga\<Down>X\<Esc>"
+ call assert_equal('-X---', getline(4))
+ autocmd! CursorMovedI *
+ quit!
+endfunc
+
diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim
new file mode 100644
index 0000000000..9f9207d27d
--- /dev/null
+++ b/src/nvim/testdir/test_help_tagjump.vim
@@ -0,0 +1,40 @@
+" Tests for :help! {subject}
+
+func SetUp()
+ " v:progpath is …/build/bin/nvim and we need …/build/runtime
+ " to be added to &rtp
+ let builddir = fnamemodify(exepath(v:progpath), ':h:h')
+ let s:rtp = &rtp
+ let &rtp .= printf(',%s/runtime', builddir)
+endfunc
+
+func TearDown()
+ let &rtp = s:rtp
+endfunc
+
+func Test_help_tagjump()
+ help
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*help.txt\*')
+ helpclose
+
+ exec "help! ('textwidth'"
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ "\\*'textwidth'\\*")
+ helpclose
+
+ exec "help! ('buflisted'),"
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ "\\*'buflisted'\\*")
+ helpclose
+
+ exec "help! abs({expr})"
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*abs()\*')
+ helpclose
+
+ exec "help! arglistid([{winnr})"
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*arglistid()\*')
+ helpclose
+endfunc
diff --git a/src/nvim/testdir/test_listlbr.in b/src/nvim/testdir/test_listlbr.in
deleted file mode 100644
index 57202b46eb..0000000000
--- a/src/nvim/testdir/test_listlbr.in
+++ /dev/null
@@ -1,81 +0,0 @@
-Test for linebreak and list option (non-utf8)
-
-STARTTEST
-:so small.vim
-:if !exists("+linebreak") | e! test.ok | w! test.out | qa! | endif
-:set wildchar=^E
-:10new|:vsp|:vert resize 20
-:put =\"\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP \"
-:norm! zt
-:set ts=4 sw=4 sts=4 linebreak sbr=+ wrap
-:fu! ScreenChar(width)
-: let c=''
-: for j in range(1,4)
-: for i in range(1,a:width)
-: let c.=nr2char(screenchar(j, i))
-: endfor
-: let c.="\n"
-: endfor
-: return c
-:endfu
-:fu! DoRecordScreen()
-: wincmd l
-: $put =printf(\"\n%s\", g:test)
-: $put =g:line
-: wincmd p
-:endfu
-:let g:test="Test 1: set linebreak"
-:redraw!
-:let line=ScreenChar(winwidth(0))
-:call DoRecordScreen()
-:let g:test="Test 2: set linebreak + set list"
-:set linebreak list listchars=
-:redraw!
-:let line=ScreenChar(winwidth(0))
-:call DoRecordScreen()
-:let g:test ="Test 3: set linebreak nolist"
-:set nolist linebreak
-:redraw!
-:let line=ScreenChar(winwidth(0))
-:call DoRecordScreen()
-:let g:test ="Test 4: set linebreak with tab and 1 line as long as screen: should break!"
-:set nolist linebreak ts=8
-:let line="1\t".repeat('a', winwidth(0)-2)
-:$put =line
-:$
-:norm! zt
-:redraw!
-:let line=ScreenChar(winwidth(0))
-:call DoRecordScreen()
-:let line="_S_\t bla"
-:$put =line
-:$
-:norm! zt
-:let g:test ="Test 5: set linebreak with conceal and set list and tab displayed by different char (line may not be truncated)"
-:set cpo&vim list linebreak conceallevel=2 concealcursor=nv listchars=tab:ab
-:syn match ConcealVar contained /_/ conceal
-:syn match All /.*/ contains=ConcealVar
-:let line=ScreenChar(winwidth(0))
-:call DoRecordScreen()
-:set cpo&vim linebreak
-:let g:test ="Test 6: set linebreak with visual block mode"
-:let line="REMOVE: this not"
-:$put =g:test
-:$put =line
-:let line="REMOVE: aaaaaaaaaaaaa"
-:$put =line
-:1/^REMOVE:
-0jf x:$put
-:set cpo&vim linebreak
-:let g:test ="Test 7: set linebreak with visual block mode and v_b_A"
-:$put =g:test
-Golong line: 40afoobar aTARGET at end
-:exe "norm! $3B\<C-v>eAx\<Esc>"
-:set cpo&vim linebreak sbr=
-:let g:test ="Test 8: set linebreak with visual char mode and changing block"
-:$put =g:test
-Go1111-1111-1111-11-1111-1111-11110f-lv3lc2222bgj.
-:%w! test.out
-:qa!
-ENDTEST
-dummy text
diff --git a/src/nvim/testdir/test_listlbr.ok b/src/nvim/testdir/test_listlbr.ok
deleted file mode 100644
index 82881234c4..0000000000
--- a/src/nvim/testdir/test_listlbr.ok
+++ /dev/null
@@ -1,43 +0,0 @@
-
- abcdef hijklmn pqrstuvwxyz_1060ABCDEFGHIJKLMNOP
-
-Test 1: set linebreak
- abcdef
-+hijklmn
-+pqrstuvwxyz_1060ABC
-+DEFGHIJKLMNOP
-
-Test 2: set linebreak + set list
-^Iabcdef hijklmn^I
-+pqrstuvwxyz_1060ABC
-+DEFGHIJKLMNOP
-
-
-Test 3: set linebreak nolist
- abcdef
-+hijklmn
-+pqrstuvwxyz_1060ABC
-+DEFGHIJKLMNOP
-1 aaaaaaaaaaaaaaaaaa
-
-Test 4: set linebreak with tab and 1 line as long as screen: should break!
-1
-+aaaaaaaaaaaaaaaaaa
-~
-~
-_S_ bla
-
-Test 5: set linebreak with conceal and set list and tab displayed by different char (line may not be truncated)
-Sabbbbbb bla
-~
-~
-~
-Test 6: set linebreak with visual block mode
-this not
-aaaaaaaaaaaaa
-REMOVE:
-REMOVE:
-Test 7: set linebreak with visual block mode and v_b_A
-long line: foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar TARGETx at end
-Test 8: set linebreak with visual char mode and changing block
-1111-2222-1111-11-1111-2222-1111
diff --git a/src/nvim/testdir/test_marks.in b/src/nvim/testdir/test_marks.in
deleted file mode 100644
index 23c2fb65fe..0000000000
--- a/src/nvim/testdir/test_marks.in
+++ /dev/null
@@ -1,34 +0,0 @@
-Tests for marks.
-
-STARTTEST
-:so small.vim
-:" test that a deleted mark is restored after delete-undo-redo-undo
-:/^\t/+1
-:set nocp viminfo+=nviminfo
-madduu
-:let a = string(getpos("'a"))
-:$put ='Mark after delete-undo-redo-undo: '.a
-:''
-ENDTEST
-
- textline A
- textline B
- textline C
-
-STARTTEST
-:" test that CTRL-A and CTRL-X updates last changed mark '[, '].
-:/^123/
-:execute "normal! \<C-A>`[v`]rAjwvjw\<C-X>`[v`]rX"
-ENDTEST
-
-CTRL-A CTRL-X:
-123 123 123
-123 123 123
-123 123 123
-
-STARTTEST
-:g/^STARTTEST/.,/^ENDTEST/d
-:wq! test.out
-ENDTEST
-
-Results:
diff --git a/src/nvim/testdir/test_marks.ok b/src/nvim/testdir/test_marks.ok
deleted file mode 100644
index e6c02ee7b0..0000000000
--- a/src/nvim/testdir/test_marks.ok
+++ /dev/null
@@ -1,16 +0,0 @@
-Tests for marks.
-
-
- textline A
- textline B
- textline C
-
-
-CTRL-A CTRL-X:
-AAA 123 123
-123 XXXXXXX
-XXX 123 123
-
-
-Results:
-Mark after delete-undo-redo-undo: [0, 15, 2, 0]
diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim
new file mode 100644
index 0000000000..be559467c8
--- /dev/null
+++ b/src/nvim/testdir/test_menu.vim
@@ -0,0 +1,9 @@
+" Test that the system menu can be loaded.
+
+func Test_load_menu()
+ try
+ source $VIMRUNTIME/menu.vim
+ catch
+ call assert_false(1, 'error while loading menus: ' . v:exception)
+ endtry
+endfunc
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
new file mode 100644
index 0000000000..9f58a35909
--- /dev/null
+++ b/src/nvim/testdir/test_timers.vim
@@ -0,0 +1,32 @@
+" Test for timers
+
+if !has('timers')
+ finish
+endif
+
+func MyHandler(timer)
+ let s:val += 1
+endfunc
+
+func Test_oneshot()
+ let s:val = 0
+ let timer = timer_start(50, 'MyHandler')
+ sleep 200m
+ call assert_equal(1, s:val)
+endfunc
+
+func Test_repeat_three()
+ let s:val = 0
+ let timer = timer_start(50, 'MyHandler', {'repeat': 3})
+ sleep 500m
+ call assert_equal(3, s:val)
+endfunc
+
+func Test_repeat_many()
+ let s:val = 0
+ let timer = timer_start(50, 'MyHandler', {'repeat': -1})
+ sleep 200m
+ call timer_stop(timer)
+ call assert_true(s:val > 1)
+ call assert_true(s:val < 5)
+endfunc
diff --git a/src/nvim/testdir/test_viml.vim b/src/nvim/testdir/test_viml.vim
new file mode 100644
index 0000000000..2d989cdad9
--- /dev/null
+++ b/src/nvim/testdir/test_viml.vim
@@ -0,0 +1,969 @@
+" Test various aspects of the Vim language.
+" Most of this was formerly in test49.
+
+"-------------------------------------------------------------------------------
+" Test environment {{{1
+"-------------------------------------------------------------------------------
+
+com! XpathINIT let g:Xpath = ''
+com! -nargs=1 -bar Xpath let g:Xpath = g:Xpath . <args>
+
+" Append a message to the "messages" file
+func! Xout(text)
+ split messages
+ $put =a:text
+ wq
+endfunc
+
+com! -nargs=1 Xout call Xout(<args>)
+
+" MakeScript() - Make a script file from a function. {{{2
+"
+" Create a script that consists of the body of the function a:funcname.
+" Replace any ":return" by a ":finish", any argument variable by a global
+" variable, and and every ":call" by a ":source" for the next following argument
+" in the variable argument list. This function is useful if similar tests are
+" to be made for a ":return" from a function call or a ":finish" in a script
+" file.
+function! MakeScript(funcname, ...)
+ let script = tempname()
+ execute "redir! >" . script
+ execute "function" a:funcname
+ redir END
+ execute "edit" script
+ " Delete the "function" and the "endfunction" lines. Do not include the
+ " word "function" in the pattern since it might be translated if LANG is
+ " set. When MakeScript() is being debugged, this deletes also the debugging
+ " output of its line 3 and 4.
+ exec '1,/.*' . a:funcname . '(.*)/d'
+ /^\d*\s*endfunction\>/,$d
+ %s/^\d*//e
+ %s/return/finish/e
+ %s/\<a:\(\h\w*\)/g:\1/ge
+ normal gg0
+ let cnt = 0
+ while search('\<call\s*\%(\u\|s:\)\w*\s*(.*)', 'W') > 0
+ let cnt = cnt + 1
+ s/\<call\s*\%(\u\|s:\)\w*\s*(.*)/\='source ' . a:{cnt}/
+ endwhile
+ g/^\s*$/d
+ write
+ bwipeout
+ return script
+endfunction
+
+" ExecAsScript - Source a temporary script made from a function. {{{2
+"
+" Make a temporary script file from the function a:funcname, ":source" it, and
+" delete it afterwards.
+function! ExecAsScript(funcname)
+ " Make a script from the function passed as argument.
+ let script = MakeScript(a:funcname)
+
+ " Source and delete the script.
+ exec "source" script
+ call delete(script)
+endfunction
+
+com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>)
+
+
+"-------------------------------------------------------------------------------
+" Test 1: :endwhile in function {{{1
+"
+" Detect if a broken loop is (incorrectly) reactivated by the
+" :endwhile. Use a :return to prevent an endless loop, and make
+" this test first to get a meaningful result on an error before other
+" tests will hang.
+"-------------------------------------------------------------------------------
+
+function! T1_F()
+ Xpath 'a'
+ let first = 1
+ while 1
+ Xpath 'b'
+ if first
+ Xpath 'c'
+ let first = 0
+ break
+ else
+ Xpath 'd'
+ return
+ endif
+ endwhile
+endfunction
+
+function! T1_G()
+ Xpath 'h'
+ let first = 1
+ while 1
+ Xpath 'i'
+ if first
+ Xpath 'j'
+ let first = 0
+ break
+ else
+ Xpath 'k'
+ return
+ endif
+ if 1 " unmatched :if
+ endwhile
+endfunction
+
+func Test_endwhile_function()
+ XpathINIT
+ call T1_F()
+ Xpath 'F'
+
+ try
+ call T1_G()
+ catch
+ " Catch missing :endif
+ call assert_true(v:exception =~ 'E171')
+ Xpath 'x'
+ endtry
+ Xpath 'G'
+
+ call assert_equal('abcFhijxG', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 2: :endwhile in script {{{1
+"
+" Detect if a broken loop is (incorrectly) reactivated by the
+" :endwhile. Use a :finish to prevent an endless loop, and place
+" this test before others that might hang to get a meaningful result
+" on an error.
+"
+" This test executes the bodies of the functions T1_F and T1_G from
+" the previous test as script files (:return replaced by :finish).
+"-------------------------------------------------------------------------------
+
+func Test_endwhile_script()
+ XpathINIT
+ ExecAsScript T1_F
+ Xpath 'F'
+
+ try
+ ExecAsScript T1_G
+ catch
+ " Catch missing :endif
+ call assert_true(v:exception =~ 'E171')
+ Xpath 'x'
+ endtry
+ Xpath 'G'
+
+ call assert_equal('abcFhijxG', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 3: :if, :elseif, :while, :continue, :break {{{1
+"-------------------------------------------------------------------------------
+
+function Test_if_while()
+ XpathINIT
+ if 1
+ Xpath 'a'
+ let loops = 3
+ while loops > -1 " main loop: loops == 3, 2, 1 (which breaks)
+ if loops <= 0
+ let break_err = 1
+ let loops = -1
+ else
+ Xpath 'b' . loops
+ endif
+ if (loops == 2)
+ while loops == 2 " dummy loop
+ Xpath 'c' . loops
+ let loops = loops - 1
+ continue " stop dummy loop
+ Xpath 'd' . loops
+ endwhile
+ continue " continue main loop
+ Xpath 'e' . loops
+ elseif (loops == 1)
+ let p = 1
+ while p " dummy loop
+ Xpath 'f' . loops
+ let p = 0
+ break " break dummy loop
+ Xpath 'g' . loops
+ endwhile
+ Xpath 'h' . loops
+ unlet p
+ break " break main loop
+ Xpath 'i' . loops
+ endif
+ if (loops > 0)
+ Xpath 'j' . loops
+ endif
+ while loops == 3 " dummy loop
+ let loops = loops - 1
+ endwhile " end dummy loop
+ endwhile " end main loop
+ Xpath 'k'
+ else
+ Xpath 'l'
+ endif
+ Xpath 'm'
+ if exists("break_err")
+ Xpath 'm'
+ unlet break_err
+ endif
+
+ unlet loops
+
+ call assert_equal('ab3j3b2c2b1f1h1km', g:Xpath)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 4: :return {{{1
+"-------------------------------------------------------------------------------
+
+function! T4_F()
+ if 1
+ Xpath 'a'
+ let loops = 3
+ while loops > 0 " 3: 2: 1:
+ Xpath 'b' . loops
+ if (loops == 2)
+ Xpath 'c' . loops
+ return
+ Xpath 'd' . loops
+ endif
+ Xpath 'e' . loops
+ let loops = loops - 1
+ endwhile
+ Xpath 'f'
+ else
+ Xpath 'g'
+ endif
+endfunction
+
+function Test_return()
+ XpathINIT
+ call T4_F()
+ Xpath '4'
+
+ call assert_equal('ab3e3b2c24', g:Xpath)
+endfunction
+
+
+"-------------------------------------------------------------------------------
+" Test 5: :finish {{{1
+"
+" This test executes the body of the function T4_F from the previous
+" test as a script file (:return replaced by :finish).
+"-------------------------------------------------------------------------------
+
+function Test_finish()
+ XpathINIT
+ ExecAsScript T4_F
+ Xpath '5'
+
+ call assert_equal('ab3e3b2c25', g:Xpath)
+endfunction
+
+
+
+"-------------------------------------------------------------------------------
+" Test 6: Defining functions in :while loops {{{1
+"
+" Functions can be defined inside other functions. An inner function
+" gets defined when the outer function is executed. Functions may
+" also be defined inside while loops. Expressions in braces for
+" defining the function name are allowed.
+"
+" The functions are defined when sourcing the script, only the
+" resulting path is checked in the test function.
+"-------------------------------------------------------------------------------
+
+XpathINIT
+
+" The command CALL collects the argument of all its invocations in "calls"
+" when used from a function (that is, when the global variable "calls" needs
+" the "g:" prefix). This is to check that the function code is skipped when
+" the function is defined. For inner functions, do so only if the outer
+" function is not being executed.
+"
+let calls = ""
+com! -nargs=1 CALL
+ \ if !exists("calls") && !exists("outer") |
+ \ let g:calls = g:calls . <args> |
+ \ endif
+
+let i = 0
+while i < 3
+ let i = i + 1
+ if i == 1
+ Xpath 'a'
+ function! F1(arg)
+ CALL a:arg
+ let outer = 1
+
+ let j = 0
+ while j < 1
+ Xpath 'b'
+ let j = j + 1
+ function! G1(arg)
+ CALL a:arg
+ endfunction
+ Xpath 'c'
+ endwhile
+ endfunction
+ Xpath 'd'
+
+ continue
+ endif
+
+ Xpath 'e' . i
+ function! F{i}(i, arg)
+ CALL a:arg
+ let outer = 1
+
+ if a:i == 3
+ Xpath 'f'
+ endif
+ let k = 0
+ while k < 3
+ Xpath 'g' . k
+ let k = k + 1
+ function! G{a:i}{k}(arg)
+ CALL a:arg
+ endfunction
+ Xpath 'h' . k
+ endwhile
+ endfunction
+ Xpath 'i'
+
+endwhile
+
+if exists("*G1")
+ Xpath 'j'
+endif
+if exists("*F1")
+ call F1("F1")
+ if exists("*G1")
+ call G1("G1")
+ endif
+endif
+
+if exists("G21") || exists("G22") || exists("G23")
+ Xpath 'k'
+endif
+if exists("*F2")
+ call F2(2, "F2")
+ if exists("*G21")
+ call G21("G21")
+ endif
+ if exists("*G22")
+ call G22("G22")
+ endif
+ if exists("*G23")
+ call G23("G23")
+ endif
+endif
+
+if exists("G31") || exists("G32") || exists("G33")
+ Xpath 'l'
+endif
+if exists("*F3")
+ call F3(3, "F3")
+ if exists("*G31")
+ call G31("G31")
+ endif
+ if exists("*G32")
+ call G32("G32")
+ endif
+ if exists("*G33")
+ call G33("G33")
+ endif
+endif
+
+Xpath 'm'
+
+let g:test6_result = g:Xpath
+let g:test6_calls = calls
+
+unlet calls
+delfunction F1
+delfunction G1
+delfunction F2
+delfunction G21
+delfunction G22
+delfunction G23
+delfunction G31
+delfunction G32
+delfunction G33
+
+function Test_defining_functions()
+ call assert_equal('ade2ie3ibcg0h1g1h2g2h3fg0h1g1h2g2h3m', g:test6_result)
+ call assert_equal('F1G1F2G21G22G23F3G31G32G33', g:test6_calls)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 7: Continuing on errors outside functions {{{1
+"
+" On an error outside a function, the script processing continues
+" at the line following the outermost :endif or :endwhile. When not
+" inside an :if or :while, the script processing continues at the next
+" line.
+"-------------------------------------------------------------------------------
+
+XpathINIT
+
+if 1
+ Xpath 'a'
+ while 1
+ Xpath 'b'
+ asdf
+ Xpath 'c'
+ break
+ endwhile | Xpath 'd'
+ Xpath 'e'
+endif | Xpath 'f'
+Xpath 'g'
+
+while 1
+ Xpath 'h'
+ if 1
+ Xpath 'i'
+ asdf
+ Xpath 'j'
+ endif | Xpath 'k'
+ Xpath 'l'
+ break
+endwhile | Xpath 'm'
+Xpath 'n'
+
+asdf
+Xpath 'o'
+
+asdf | Xpath 'p'
+Xpath 'q'
+
+let g:test7_result = g:Xpath
+
+func Test_error_in_script()
+ call assert_equal('abghinoq', g:test7_result)
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 8: Aborting and continuing on errors inside functions {{{1
+"
+" On an error inside a function without the "abort" attribute, the
+" script processing continues at the next line (unless the error was
+" in a :return command). On an error inside a function with the
+" "abort" attribute, the function is aborted and the script processing
+" continues after the function call; the value -1 is returned then.
+"-------------------------------------------------------------------------------
+
+XpathINIT
+
+function! T8_F()
+ if 1
+ Xpath 'a'
+ while 1
+ Xpath 'b'
+ asdf
+ Xpath 'c'
+ asdf | Xpath 'd'
+ Xpath 'e'
+ break
+ endwhile
+ Xpath 'f'
+ endif | Xpath 'g'
+ Xpath 'h'
+
+ while 1
+ Xpath 'i'
+ if 1
+ Xpath 'j'
+ asdf
+ Xpath 'k'
+ asdf | Xpath 'l'
+ Xpath 'm'
+ endif
+ Xpath 'n'
+ break
+ endwhile | Xpath 'o'
+ Xpath 'p'
+
+ return novar " returns (default return value 0)
+ Xpath 'q'
+ return 1 " not reached
+endfunction
+
+function! T8_G() abort
+ if 1
+ Xpath 'r'
+ while 1
+ Xpath 's'
+ asdf " returns -1
+ Xpath 't'
+ break
+ endwhile
+ Xpath 'v'
+ endif | Xpath 'w'
+ Xpath 'x'
+
+ return -4 " not reached
+endfunction
+
+function! T8_H() abort
+ while 1
+ Xpath 'A'
+ if 1
+ Xpath 'B'
+ asdf " returns -1
+ Xpath 'C'
+ endif
+ Xpath 'D'
+ break
+ endwhile | Xpath 'E'
+ Xpath 'F'
+
+ return -4 " not reached
+endfunction
+
+" Aborted functions (T8_G and T8_H) return -1.
+let g:test8_sum = (T8_F() + 1) - 4 * T8_G() - 8 * T8_H()
+Xpath 'X'
+let g:test8_result = g:Xpath
+
+func Test_error_in_function()
+ call assert_equal(13, g:test8_sum)
+ call assert_equal('abcefghijkmnoprsABX', g:test8_result)
+
+ delfunction T8_F
+ delfunction T8_G
+ delfunction T8_H
+endfunc
+
+
+"-------------------------------------------------------------------------------
+" Test 9: Continuing after aborted functions {{{1
+"
+" When a function with the "abort" attribute is aborted due to an
+" error, the next function back in the call hierarchy without an
+" "abort" attribute continues; the value -1 is returned then.
+"-------------------------------------------------------------------------------
+
+XpathINIT
+
+function! F() abort
+ Xpath 'a'
+ let result = G() " not aborted
+ Xpath 'b'
+ if result != 2
+ Xpath 'c'
+ endif
+ return 1
+endfunction
+
+function! G() " no abort attribute
+ Xpath 'd'
+ if H() != -1 " aborted
+ Xpath 'e'
+ endif
+ Xpath 'f'
+ return 2
+endfunction
+
+function! H() abort
+ Xpath 'g'
+ call I() " aborted
+ Xpath 'h'
+ return 4
+endfunction
+
+function! I() abort
+ Xpath 'i'
+ asdf " error
+ Xpath 'j'
+ return 8
+endfunction
+
+if F() != 1
+ Xpath 'k'
+endif
+
+let g:test9_result = g:Xpath
+
+delfunction F
+delfunction G
+delfunction H
+delfunction I
+
+func Test_func_abort()
+ call assert_equal('adgifb', g:test9_result)
+endfunc
+
+
+"-------------------------------------------------------------------------------
+" Test 10: :if, :elseif, :while argument parsing {{{1
+"
+" A '"' or '|' in an argument expression must not be mixed up with
+" a comment or a next command after a bar. Parsing errors should
+" be recognized.
+"-------------------------------------------------------------------------------
+
+XpathINIT
+
+function! MSG(enr, emsg)
+ let english = v:lang == "C" || v:lang =~ '^[Ee]n'
+ if a:enr == ""
+ Xout "TODO: Add message number for:" a:emsg
+ let v:errmsg = ":" . v:errmsg
+ endif
+ let match = 1
+ if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
+ let match = 0
+ if v:errmsg == ""
+ Xout "Message missing."
+ else
+ let v:errmsg = escape(v:errmsg, '"')
+ Xout "Unexpected message:" v:errmsg
+ endif
+ endif
+ return match
+endfunction
+
+if 1 || strlen("\"") | Xpath 'a'
+ Xpath 'b'
+endif
+Xpath 'c'
+
+if 0
+elseif 1 || strlen("\"") | Xpath 'd'
+ Xpath 'e'
+endif
+Xpath 'f'
+
+while 1 || strlen("\"") | Xpath 'g'
+ Xpath 'h'
+ break
+endwhile
+Xpath 'i'
+
+let v:errmsg = ""
+if 1 ||| strlen("\"") | Xpath 'j'
+ Xpath 'k'
+endif
+Xpath 'l'
+if !MSG('E15', "Invalid expression")
+ Xpath 'm'
+endif
+
+let v:errmsg = ""
+if 0
+elseif 1 ||| strlen("\"") | Xpath 'n'
+ Xpath 'o'
+endif
+Xpath 'p'
+if !MSG('E15', "Invalid expression")
+ Xpath 'q'
+endif
+
+let v:errmsg = ""
+while 1 ||| strlen("\"") | Xpath 'r'
+ Xpath 's'
+ break
+endwhile
+Xpath 't'
+if !MSG('E15', "Invalid expression")
+ Xpath 'u'
+endif
+
+let g:test10_result = g:Xpath
+delfunction MSG
+
+func Test_expr_parsing()
+ call assert_equal('abcdefghilpt', g:test10_result)
+endfunc
+
+
+"-------------------------------------------------------------------------------
+" Test 11: :if, :elseif, :while argument evaluation after abort {{{1
+"
+" When code is skipped over due to an error, the boolean argument to
+" an :if, :elseif, or :while must not be evaluated.
+"-------------------------------------------------------------------------------
+
+XpathINIT
+
+let calls = 0
+
+function! P(num)
+ let g:calls = g:calls + a:num " side effect on call
+ return 0
+endfunction
+
+if 1
+ Xpath 'a'
+ asdf " error
+ Xpath 'b'
+ if P(1) " should not be called
+ Xpath 'c'
+ elseif !P(2) " should not be called
+ Xpath 'd'
+ else
+ Xpath 'e'
+ endif
+ Xpath 'f'
+ while P(4) " should not be called
+ Xpath 'g'
+ endwhile
+ Xpath 'h'
+endif
+Xpath 'x'
+
+let g:test11_calls = calls
+let g:test11_result = g:Xpath
+
+unlet calls
+delfunction P
+
+func Test_arg_abort()
+ call assert_equal(0, g:test11_calls)
+ call assert_equal('ax', g:test11_result)
+endfunc
+
+
+"-------------------------------------------------------------------------------
+" Test 12: Expressions in braces in skipped code {{{1
+"
+" In code skipped over due to an error or inactive conditional,
+" an expression in braces as part of a variable or function name
+" should not be evaluated.
+"-------------------------------------------------------------------------------
+
+XpathINIT
+
+function! NULL()
+ Xpath 'a'
+ return 0
+endfunction
+
+function! ZERO()
+ Xpath 'b'
+ return 0
+endfunction
+
+function! F0()
+ Xpath 'c'
+endfunction
+
+function! F1(arg)
+ Xpath 'e'
+endfunction
+
+let V0 = 1
+
+Xpath 'f'
+echo 0 ? F{NULL() + V{ZERO()}}() : 1
+
+Xpath 'g'
+if 0
+ Xpath 'h'
+ call F{NULL() + V{ZERO()}}()
+endif
+
+Xpath 'i'
+if 1
+ asdf " error
+ Xpath 'j'
+ call F1(F{NULL() + V{ZERO()}}())
+endif
+
+Xpath 'k'
+if 1
+ asdf " error
+ Xpath 'l'
+ call F{NULL() + V{ZERO()}}()
+endif
+
+let g:test12_result = g:Xpath
+
+func Test_braces_skipped()
+ call assert_equal('fgik', g:test12_result)
+endfunc
+
+
+"-------------------------------------------------------------------------------
+" Test 13: Failure in argument evaluation for :while {{{1
+"
+" A failure in the expression evaluation for the condition of a :while
+" causes the whole :while loop until the matching :endwhile being
+" ignored. Continuation is at the next following line.
+"-------------------------------------------------------------------------------
+
+XpathINIT
+
+Xpath 'a'
+while asdf
+ Xpath 'b'
+ while 1
+ Xpath 'c'
+ break
+ endwhile
+ Xpath 'd'
+ break
+endwhile
+Xpath 'e'
+
+while asdf | Xpath 'f' | endwhile | Xpath 'g'
+Xpath 'h'
+let g:test13_result = g:Xpath
+
+func Test_while_fail()
+ call assert_equal('aeh', g:test13_result)
+endfunc
+
+
+"-------------------------------------------------------------------------------
+" Test 14: Failure in argument evaluation for :if {{{1
+"
+" A failure in the expression evaluation for the condition of an :if
+" does not cause the corresponding :else or :endif being matched to
+" a previous :if/:elseif. Neither of both branches of the failed :if
+" are executed.
+"-------------------------------------------------------------------------------
+
+XpathINIT
+
+function! F()
+ Xpath 'a'
+ let x = 0
+ if x " false
+ Xpath 'b'
+ elseif !x " always true
+ Xpath 'c'
+ let x = 1
+ if g:boolvar " possibly undefined
+ Xpath 'd'
+ else
+ Xpath 'e'
+ endif
+ Xpath 'f'
+ elseif x " never executed
+ Xpath 'g'
+ endif
+ Xpath 'h'
+endfunction
+
+let boolvar = 1
+call F()
+Xpath '-'
+
+unlet boolvar
+call F()
+let g:test14_result = g:Xpath
+
+delfunction F
+
+func Test_if_fail()
+ call assert_equal('acdfh-acfh', g:test14_result)
+endfunc
+
+
+"-------------------------------------------------------------------------------
+" Test 15: Failure in argument evaluation for :if (bar) {{{1
+"
+" Like previous test, except that the failing :if ... | ... | :endif
+" is in a single line.
+"-------------------------------------------------------------------------------
+
+XpathINIT
+
+function! F()
+ Xpath 'a'
+ let x = 0
+ if x " false
+ Xpath 'b'
+ elseif !x " always true
+ Xpath 'c'
+ let x = 1
+ if g:boolvar | Xpath 'd' | else | Xpath 'e' | endif
+ Xpath 'f'
+ elseif x " never executed
+ Xpath 'g'
+ endif
+ Xpath 'h'
+endfunction
+
+let boolvar = 1
+call F()
+Xpath '-'
+
+unlet boolvar
+call F()
+let g:test15_result = g:Xpath
+
+delfunction F
+
+func Test_if_bar_fail()
+ call assert_equal('acdfh-acfh', g:test15_result)
+endfunc
+
+
+"-------------------------------------------------------------------------------
+" Test 16: Recognizing {} in variable name. {{{1
+"-------------------------------------------------------------------------------
+
+func Test_curlies()
+ let s:var = 66
+ let ns = 's'
+ call assert_equal(66, {ns}:var)
+
+ let g:a = {}
+ let g:b = 't'
+ let g:a[g:b] = 77
+ call assert_equal(77, g:a['t'])
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 91: using type(). {{{1
+"-------------------------------------------------------------------------------
+
+func Test_type()
+ call assert_equal(0, type(0))
+ call assert_equal(1, type(""))
+ call assert_equal(2, type(function("tr")))
+ call assert_equal(3, type([]))
+ call assert_equal(4, type({}))
+ call assert_equal(5, type(0.0))
+ call assert_equal(6, type(v:false))
+ call assert_equal(6, type(v:true))
+ call assert_equal(7, type(v:null))
+endfunc
+
+"-------------------------------------------------------------------------------
+" Test 92: skipping code {{{1
+"-------------------------------------------------------------------------------
+
+func Test_skip()
+ let Fn = function('Test_type')
+ call assert_false(0 && Fn[1])
+ call assert_false(0 && string(Fn))
+ call assert_false(0 && len(Fn))
+ let l = []
+ call assert_false(0 && l[1])
+ call assert_false(0 && string(l))
+ call assert_false(0 && len(l))
+ let f = 1.0
+ call assert_false(0 && f[1])
+ call assert_false(0 && string(f))
+ call assert_false(0 && len(f))
+ let sp = v:null
+ call assert_false(0 && sp[1])
+ call assert_false(0 && string(sp))
+ call assert_false(0 && len(sp))
+
+endfunc
+
+"-------------------------------------------------------------------------------
+" Modelines {{{1
+" vim: ts=8 sw=4 tw=80 fdm=marker
+" vim: fdt=substitute(substitute(foldtext(),\ '\\%(^+--\\)\\@<=\\(\\s*\\)\\(.\\{-}\\)\:\ \\%(\"\ \\)\\=\\(Test\ \\d*\\)\:\\s*',\ '\\3\ (\\2)\:\ \\1',\ \"\"),\ '\\(Test\\s*\\)\\(\\d\\)\\D\\@=',\ '\\1\ \\2',\ "")
+"-------------------------------------------------------------------------------
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index b41e4d2fba..99eb230a88 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -224,9 +224,9 @@ static void tk_getkeys(TermInput *input, bool force)
while ((result = tk_getkey(input->tk, &key, force)) == TERMKEY_RES_KEY) {
if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) {
forward_simple_utf8(input, &key);
- } else if (key.type == TERMKEY_TYPE_UNICODE ||
- key.type == TERMKEY_TYPE_FUNCTION ||
- key.type == TERMKEY_TYPE_KEYSYM) {
+ } else if (key.type == TERMKEY_TYPE_UNICODE
+ || key.type == TERMKEY_TYPE_FUNCTION
+ || key.type == TERMKEY_TYPE_KEYSYM) {
forward_modified_utf8(input, &key);
} else if (key.type == TERMKEY_TYPE_MOUSE) {
forward_mouse_event(input, &key);
@@ -282,9 +282,9 @@ static bool handle_focus_event(TermInput *input)
static bool handle_bracketed_paste(TermInput *input)
{
- if (rbuffer_size(input->read_stream.buffer) > 5 &&
- (!rbuffer_cmp(input->read_stream.buffer, "\x1b[200~", 6) ||
- !rbuffer_cmp(input->read_stream.buffer, "\x1b[201~", 6))) {
+ if (rbuffer_size(input->read_stream.buffer) > 5
+ && (!rbuffer_cmp(input->read_stream.buffer, "\x1b[200~", 6)
+ || !rbuffer_cmp(input->read_stream.buffer, "\x1b[201~", 6))) {
bool enable = *rbuffer_get(input->read_stream.buffer, 4) == '0';
// Advance past the sequence
rbuffer_consumed(input->read_stream.buffer, 6);
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 00e2821075..62bc81ba64 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -81,7 +81,7 @@ UI *tui_start(void)
{
UI *ui = xcalloc(1, sizeof(UI));
ui->stop = tui_stop;
- ui->rgb = os_getenv("NVIM_TUI_ENABLE_TRUE_COLOR") != NULL;
+ ui->rgb = p_tgc;
ui->resize = tui_resize;
ui->clear = tui_clear;
ui->eol_clear = tui_eol_clear;
@@ -100,6 +100,7 @@ UI *tui_start(void)
ui->visual_bell = tui_visual_bell;
ui->update_fg = tui_update_fg;
ui->update_bg = tui_update_bg;
+ ui->update_sp = tui_update_sp;
ui->flush = tui_flush;
ui->suspend = tui_suspend;
ui->set_title = tui_set_title;
@@ -573,6 +574,11 @@ static void tui_update_bg(UI *ui, int bg)
((TUIData *)ui->data)->grid.bg = bg;
}
+static void tui_update_sp(UI *ui, int sp)
+{
+ // Do nothing; 'special' color is for GUI only
+}
+
static void tui_flush(UI *ui)
{
TUIData *data = ui->data;
@@ -628,8 +634,8 @@ static void tui_suspend(UI *ui)
static void tui_set_title(UI *ui, char *title)
{
TUIData *data = ui->data;
- if (!(title && unibi_get_str(data->ut, unibi_to_status_line) &&
- unibi_get_str(data->ut, unibi_from_status_line))) {
+ if (!(title && unibi_get_str(data->ut, unibi_to_status_line)
+ && unibi_get_str(data->ut, unibi_from_status_line))) {
return;
}
unibi_out(ui, unibi_to_status_line);
@@ -694,8 +700,8 @@ static void update_size(UI *ui)
}
// 2 - try from a system call(ioctl/TIOCGWINSZ on unix)
- if (data->out_isatty &&
- !uv_tty_get_winsize(&data->output_handle.tty, &width, &height)) {
+ if (data->out_isatty
+ && !uv_tty_get_winsize(&data->output_handle.tty, &width, &height)) {
goto end;
}
diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h
index df51e1fced..ad6d96a168 100644
--- a/src/nvim/ugrid.h
+++ b/src/nvim/ugrid.h
@@ -21,7 +21,7 @@ struct ugrid {
UCell **cells;
};
-#define EMPTY_ATTRS ((HlAttrs){false, false, false, false, false, -1, -1})
+#define EMPTY_ATTRS ((HlAttrs){ false, false, false, false, false, -1, -1, -1 })
#define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \
do { \
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index d32969f149..ae38754c1e 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -30,7 +30,11 @@
#include "nvim/screen.h"
#include "nvim/syntax.h"
#include "nvim/window.h"
-#include "nvim/tui/tui.h"
+#ifdef FEAT_TUI
+# include "nvim/tui/tui.h"
+#else
+# include "nvim/msgpack_rpc/server.h"
+#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui.c.generated.h"
@@ -83,7 +87,22 @@ static int height, width;
void ui_builtin_start(void)
{
+#ifdef FEAT_TUI
tui_start();
+#else
+ fprintf(stderr, "Neovim was built without a Terminal UI," \
+ "press Ctrl+C to exit\n");
+
+ size_t len;
+ char **addrs = server_address_list(&len);
+ if (addrs != NULL) {
+ fprintf(stderr, "currently listening on the following address(es)\n");
+ for (size_t i = 0; i < len; i++) {
+ fprintf(stderr, "\t%s\n", addrs[i]);
+ }
+ xfree(addrs);
+ }
+#endif
}
void ui_builtin_stop(void)
@@ -155,6 +174,7 @@ void ui_resize(int new_width, int new_height)
UI_CALL(update_fg, (ui->rgb ? normal_fg : cterm_normal_fg_color - 1));
UI_CALL(update_bg, (ui->rgb ? normal_bg : cterm_normal_bg_color - 1));
+ UI_CALL(update_sp, (ui->rgb ? normal_sp : -1));
sr.top = 0;
sr.bot = height - 1;
@@ -187,7 +207,7 @@ void ui_mouse_off(void)
UI_CALL(mouse_off);
}
-void ui_attach(UI *ui)
+void ui_attach_impl(UI *ui)
{
if (ui_count == MAX_UI_COUNT) {
abort();
@@ -197,7 +217,7 @@ void ui_attach(UI *ui)
ui_refresh();
}
-void ui_detach(UI *ui)
+void ui_detach_impl(UI *ui)
{
size_t shift_index = MAX_UI_COUNT;
@@ -388,7 +408,7 @@ static void parse_control_character(uint8_t c)
static void set_highlight_args(int attr_code)
{
- HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1 };
+ HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1, -1 };
HlAttrs cterm_attrs = rgb_attrs;
if (attr_code == HL_NORMAL) {
@@ -425,6 +445,10 @@ static void set_highlight_args(int attr_code)
rgb_attrs.background = aep->rgb_bg_color;
}
+ if (aep->rgb_sp_color != normal_sp) {
+ rgb_attrs.special = aep->rgb_sp_color;
+ }
+
if (cterm_normal_fg_color != aep->cterm_fg_color) {
cterm_attrs.foreground = aep->cterm_fg_color - 1;
}
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 4c051fcfbf..5934d2fee9 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -7,7 +7,7 @@
typedef struct {
bool bold, underline, undercurl, italic, reverse;
- int foreground, background;
+ int foreground, background, special;
} HlAttrs;
typedef struct ui_t UI;
@@ -35,6 +35,7 @@ struct ui_t {
void (*flush)(UI *ui);
void (*update_fg)(UI *ui, int fg);
void (*update_bg)(UI *ui, int bg);
+ void (*update_sp)(UI *ui, int sp);
void (*suspend)(UI *ui);
void (*set_title)(UI *ui, char *title);
void (*set_icon)(UI *ui, char *icon);
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index 359fffe3bf..d17fa4a782 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -49,6 +49,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
rv->bridge.visual_bell = ui_bridge_visual_bell;
rv->bridge.update_fg = ui_bridge_update_fg;
rv->bridge.update_bg = ui_bridge_update_bg;
+ rv->bridge.update_sp = ui_bridge_update_sp;
rv->bridge.flush = ui_bridge_flush;
rv->bridge.suspend = ui_bridge_suspend;
rv->bridge.set_title = ui_bridge_set_title;
@@ -70,7 +71,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
}
uv_mutex_unlock(&rv->mutex);
- ui_attach(&rv->bridge);
+ ui_attach_impl(&rv->bridge);
return &rv->bridge;
}
@@ -104,7 +105,7 @@ static void ui_bridge_stop(UI *b)
uv_thread_join(&bridge->ui_thread);
uv_mutex_destroy(&bridge->mutex);
uv_cond_destroy(&bridge->cond);
- ui_detach(b);
+ ui_detach_impl(b);
xfree(b);
}
static void ui_bridge_stop_event(void **argv)
@@ -305,6 +306,16 @@ static void ui_bridge_update_bg_event(void **argv)
ui->update_bg(ui, PTR2INT(argv[1]));
}
+static void ui_bridge_update_sp(UI *b, int sp)
+{
+ UI_CALL(b, update_sp, 2, b, INT2PTR(sp));
+}
+static void ui_bridge_update_sp_event(void **argv)
+{
+ UI *ui = UI(argv[0]);
+ ui->update_sp(ui, PTR2INT(argv[1]));
+}
+
static void ui_bridge_flush(UI *b)
{
UI_CALL(b, flush, 1, b);
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index b8cdffcda0..f16b4264d7 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -4,29 +4,29 @@
* The saved lines are stored in a list of lists (one for each buffer):
*
* b_u_oldhead------------------------------------------------+
- * |
- * V
- * +--------------+ +--------------+ +--------------+
- * b_u_newhead--->| u_header | | u_header | | u_header |
- * | uh_next------>| uh_next------>| uh_next---->NULL
- * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
- * | uh_entry | | uh_entry | | uh_entry |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ +--------------+ +--------------+
- * | u_entry | | u_entry | | u_entry |
- * | ue_next | | ue_next | | ue_next |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ NULL NULL
- * | u_entry |
- * | ue_next |
- * +--------|-----+
- * |
- * V
- * etc.
+ * |
+ * V
+ * +--------------+ +--------------+ +--------------+
+ * b_u_newhead--->| u_header | | u_header | | u_header |
+ * | uh_next------>| uh_next------>| uh_next---->NULL
+ * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
+ * | uh_entry | | uh_entry | | uh_entry |
+ * +--------|-----+ +--------|-----+ +--------|-----+
+ * | | |
+ * V V V
+ * +--------------+ +--------------+ +--------------+
+ * | u_entry | | u_entry | | u_entry |
+ * | ue_next | | ue_next | | ue_next |
+ * +--------|-----+ +--------|-----+ +--------|-----+
+ * | | |
+ * V V V
+ * +--------------+ NULL NULL
+ * | u_entry |
+ * | ue_next |
+ * +--------|-----+
+ * |
+ * V
+ * etc.
*
* Each u_entry list contains the information for one undo or redo.
* curbuf->b_u_curhead points to the header of the last undo (the next redo),
@@ -37,30 +37,30 @@
* uh_seq field is numbered sequentially to be able to find a newer or older
* branch.
*
- * +---------------+ +---------------+
- * b_u_oldhead --->| u_header | | u_header |
- * | uh_alt_next ---->| uh_alt_next ----> NULL
- * NULL <----- uh_alt_prev |<------ uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * V V
- * +---------------+ +---------------+
- * | u_header | | u_header |
- * | uh_alt_next | | uh_alt_next |
- * b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * V V
- * NULL +---------------+ +---------------+
- * | u_header | | u_header |
- * | uh_alt_next ---->| uh_alt_next |
- * | uh_alt_prev |<------ uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * etc. etc.
+ * +---------------+ +---------------+
+ * b_u_oldhead --->| u_header | | u_header |
+ * | uh_alt_next ---->| uh_alt_next ----> NULL
+ * NULL <----- uh_alt_prev |<------ uh_alt_prev |
+ * | uh_prev | | uh_prev |
+ * +-----|---------+ +-----|---------+
+ * | |
+ * V V
+ * +---------------+ +---------------+
+ * | u_header | | u_header |
+ * | uh_alt_next | | uh_alt_next |
+ * b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
+ * | uh_prev | | uh_prev |
+ * +-----|---------+ +-----|---------+
+ * | |
+ * V V
+ * NULL +---------------+ +---------------+
+ * | u_header | | u_header |
+ * | uh_alt_next ---->| uh_alt_next |
+ * | uh_alt_prev |<------ uh_alt_prev |
+ * | uh_prev | | uh_prev |
+ * +-----|---------+ +-----|---------+
+ * | |
+ * etc. etc.
*
*
* All data is allocated and will all be freed when the buffer is unloaded.
@@ -1782,7 +1782,13 @@ void undo_time(long step, int sec, int file, int absolute)
/* "target" is the node below which we want to be.
* Init "closest" to a value we can't reach. */
if (absolute) {
- target = step;
+ if (step == 0) {
+ // target 0 does not exist, got to 1 and above it.
+ target = 1;
+ above = true;
+ } else {
+ target = step;
+ }
closest = -1;
} else {
/* When doing computations with time_t subtract starttime, because
@@ -2870,9 +2876,7 @@ static char_u *u_save_line(linenr_T lnum)
/// @return true if the buffer has changed
bool bufIsChanged(buf_T *buf)
{
- return
- !bt_dontwrite(buf) &&
- (buf->b_changed || file_ff_differs(buf, true));
+ return !bt_dontwrite(buf) && (buf->b_changed || file_ff_differs(buf, true));
}
/// Check if the 'modified' flag is set, or 'ff' has changed (only need to
@@ -2882,9 +2886,8 @@ bool bufIsChanged(buf_T *buf)
/// @return true if the current buffer has changed
bool curbufIsChanged(void)
{
- return
- !bt_dontwrite(curbuf) &&
- (curbuf->b_changed || file_ff_differs(curbuf, true));
+ return (!bt_dontwrite(curbuf)
+ && (curbuf->b_changed || file_ff_differs(curbuf, true)));
}
/*
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 026eab46d0..844c21916f 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -64,11 +64,415 @@ static char *features[] = {
#else
"-jemalloc",
#endif
+
+#ifdef FEAT_TUI
+ "+tui",
+#else
+ "-tui",
+#endif
NULL
};
// clang-format off
static int included_patches[] = {
+ 1832,
+ 1831,
+ 1809,
+ 1808,
+ 1806,
+ 1799,
+ 1757,
+ 1755,
+ 1753,
+ 1728,
+ 1654,
+ 1652,
+ 1643,
+ 1641,
+ // 1624 NA
+
+ // 1600 NA
+ // 1599 NA
+ // 1598 NA
+ // 1597 NA
+ // 1596,
+ // 1595 NA
+ // 1594 NA
+ // 1593 NA
+ // 1592,
+ // 1591,
+ // 1590,
+ // 1589,
+ // 1588,
+ // 1587 NA
+ // 1586,
+ // 1585,
+ // 1584 NA
+ // 1583 NA
+ // 1582,
+
+ // 1581,
+ // 1580,
+ // 1579,
+ 1578,
+ // 1577,
+ 1576,
+ // 1575 NA
+ 1574,
+ // 1573,
+ // 1572 NA
+ 1571,
+ 1570,
+ 1569,
+ 1568,
+ 1567,
+ // 1566 NA
+ // 1565,
+ // 1564,
+ // 1563,
+ // 1562 NA
+ // 1561 NA
+ // 1560,
+ // 1559,
+ // 1558,
+ // 1557,
+ // 1556,
+ // 1555,
+ // 1554,
+ // 1553,
+ // 1552,
+ // 1551,
+ // 1550,
+ // 1549,
+ // 1548,
+ // 1547,
+ // 1546,
+ // 1545 NA
+ // 1544 NA
+ // 1543 NA
+ // 1542 NA
+ // 1541 NA
+ // 1540 NA
+ // 1539 NA
+ // 1538 NA
+ // 1537 NA
+ // 1536 NA
+ // 1535,
+ // 1534 NA
+ // 1533,
+ // 1532 NA
+ // 1531 NA
+ // 1530 NA
+ // 1529 NA
+ // 1528,
+ // 1527 NA
+ // 1526 NA
+ // 1525 NA
+ // 1524 NA
+ // 1523 NA
+ // 1522 NA
+ 1521,
+ // 1520 NA
+ // 1519 NA
+ // 1518 NA
+ // 1517 NA
+ 1516,
+ // 1515 NA
+ // 1514 NA
+ 1513,
+ // 1512 NA
+ 1511,
+ // 1510 NA
+ // 1509 NA
+ // 1508 NA
+ // 1507 NA
+ // 1506 NA
+ // 1505 NA
+ // 1504 NA
+ // 1503 NA
+ // 1502 NA
+ // 1501 NA
+ 1500,
+ // 1499,
+ // 1498 NA
+ // 1497 NA
+ // 1496 NA
+ // 1495 NA
+ // 1494,
+ // 1493 NA
+ // 1492,
+ // 1491,
+ // 1490 NA
+ // 1489 NA
+ // 1488 NA
+ // 1487 NA
+ // 1486,
+ // 1485 NA
+ // 1484 NA
+ // 1483 NA
+ // 1482 NA
+ // 1481 NA
+ // 1480,
+ // 1479,
+ // 1478,
+ // 1477,
+ // 1476 NA
+ // 1475 NA
+ // 1474 NA
+ // 1473 NA
+ // 1472 NA
+ // 1471 NA
+ // 1470 NA
+ // 1469 NA
+ // 1468,
+ // 1467 NA
+ // 1466 NA
+ // 1465 NA
+ // 1464,
+ // 1463 NA
+ // 1462 NA
+ // 1461 NA
+ // 1460 NA
+ // 1459 NA
+ // 1458 NA
+ // 1457 NA
+ // 1456,
+ // 1455 NA
+ // 1454 NA
+ // 1453 NA
+ // 1452 NA
+ // 1451 NA
+ // 1450 NA
+ // 1449 NA
+ // 1448 NA
+ // 1447 NA
+ // 1446 NA
+ // 1445 NA
+ // 1444 NA
+ // 1443 NA
+ // 1442 NA
+ // 1441 NA
+ // 1440 NA
+ // 1439 NA
+ // 1438 NA
+ // 1437 NA
+ // 1436 NA
+ // 1435 NA
+ // 1434 NA
+ // 1433 NA
+ // 1432 NA
+ // 1431 NA
+ // 1430 NA
+ // 1429 NA
+ // 1428 NA
+ // 1427 NA
+ // 1426 NA
+ 1425,
+ // 1424 NA
+ // 1423 NA
+ // 1422 NA
+ // 1421 NA
+ // 1420 NA
+ // 1419 NA
+ // 1418 NA
+ // 1417 NA
+ // 1416 NA
+ // 1415 NA
+ // 1414 NA
+ // 1413 NA
+ // 1412 NA
+ // 1411 NA
+ 1410,
+ // 1409 NA
+ // 1408 NA
+ // 1407 NA
+ 1406,
+ 1405,
+ // 1404 NA
+ // 1403 NA
+ // 1402 NA
+ 1401,
+ // 1400 NA
+ // 1399 NA
+ // 1398 NA
+ // 1397,
+ // 1396,
+ // 1395 NA
+ // 1394,
+ // 1393 NA
+ // 1392 NA
+ // 1391 NA
+ // 1390 NA
+ // 1389 NA
+ // 1388,
+ // 1387 NA
+ // 1386 NA
+ // 1385 NA
+ // 1384,
+ // 1383 NA
+ // 1382 NA
+ // 1381 NA
+ // 1380 NA
+ // 1379 NA
+ // 1378 NA
+ // 1377 NA
+ // 1376 NA
+ // 1375 NA
+ // 1374 NA
+ // 1373 NA
+ // 1372 NA
+ // 1371 NA
+ // 1370 NA
+ // 1369 NA
+ // 1368 NA
+ // 1367 NA
+ 1366,
+ // 1365,
+ // 1364 NA
+ // 1363 NA
+ // 1362 NA
+ // 1361 NA
+ // 1360 NA
+ // 1359 NA
+ // 1358 NA
+ // 1357 NA
+ // 1356 NA
+ // 1355 NA
+ // 1354 NA
+ // 1353 NA
+ // 1352,
+ // 1351 NA
+ // 1350 NA
+ // 1349 NA
+ // 1348 NA
+ 1347,
+ 1346,
+ // 1345 NA
+ // 1344 NA
+ // 1343 NA
+ // 1342 NA
+ // 1341 NA
+ // 1340 NA
+ // 1339 NA
+ // 1338 NA
+ // 1337 NA
+ // 1336 NA
+ // 1335 NA
+ // 1334 NA
+ // 1333 NA
+ // 1332 NA
+ // 1331 NA
+ // 1330 NA
+ // 1329 NA
+ // 1328 NA
+ // 1327 NA
+ // 1326 NA
+ // 1325 NA
+ // 1324 NA
+ // 1323 NA
+ // 1322 NA
+ // 1321 NA
+ // 1320 NA
+ // 1319 NA
+ // 1318 NA
+ // 1317 NA
+ // 1316 NA
+ // 1315 NA
+ // 1314 NA
+ // 1313 NA
+ // 1312 NA
+ // 1311 NA
+ // 1310 NA
+ 1309,
+ // 1308 NA
+ // 1307 NA
+ // 1306 NA
+ // 1305,
+ 1304,
+ // 1303 NA
+ // 1302 NA
+ // 1301 NA
+ // 1300 NA
+ // 1299 NA
+ // 1298 NA
+ // 1297 NA
+ 1296,
+ // 1295 NA
+ // 1294 NA
+ // 1293 NA
+ 1292,
+ // 1291 NA
+ // 1290 NA
+ // 1289 NA
+ // 1288 NA
+ // 1287 NA
+ // 1286 NA
+ 1285,
+ 1284,
+ // 1283 NA
+ 1282,
+ 1281,
+ // 1280 NA
+ // 1279 NA
+ // 1278 NA
+ // 1277 NA
+ 1276,
+ // 1275 NA
+ // 1274 NA
+ // 1273,
+ // 1272 NA
+ 1271,
+ // 1270 NA
+ 1269,
+ // 1268 NA
+ 1267,
+ // 1266
+ // 1265 NA
+ // 1264 NA
+ // 1263 NA
+ // 1262 NA
+ // 1261 NA
+ // 1260 NA
+ 1259,
+ // 1258 NA
+ // 1257 NA
+ // 1256 NA
+ // 1255 NA
+ // 1254 NA
+ // 1253 NA
+ // 1252 NA
+ // 1251 NA
+ // 1250 NA
+ // 1249 NA
+ // 1248 NA
+ // 1247 NA
+ // 1246 NA
+ // 1245 NA
+ // 1244 NA
+ // 1243 NA
+ // 1242 NA
+ // 1241 NA
+ // 1240 NA
+ // 1239 NA
+ // 1238 NA
+ // 1237,
+ 1236,
+ // 1235 NA
+ // 1234 NA
+ // 1233 NA
+ // 1232 NA
+ // 1231 NA
+ // 1230 NA
+ // 1229 NA
+ 1228,
+ // 1227 NA
+ // 1226 NA
+ // 1225 NA
+ // 1224 NA
+ // 1223,
+ // 1222 NA
+ // 1221 NA
+ // 1220 NA
// 1219 NA
// 1218 NA
// 1217 NA
@@ -100,41 +504,41 @@ static int included_patches[] = {
// 1191 NA
// 1190 NA
// 1189 NA
- // 1188,
+ // 1188 NA
// 1187 NA
// 1186,
// 1185 NA
// 1184 NA
// 1183 NA
// 1182 NA
- // 1181,
+ 1181,
1180,
// 1179,
- // 1178,
+ 1178,
// 1177 NA
// 1176 NA
// 1175 NA
// 1174 NA
- // 1173,
+ 1173,
// 1172 NA
// 1171 NA
// 1170 NA
// 1169 NA
- // 1168,
- // 1167,
- // 1166,
+ 1168,
+ 1167,
+ 1166,
// 1165 NA
- // 1164,
- // 1163,
+ 1164,
+ 1163,
// 1162 NA
- // 1161,
- // 1160,
+ 1161,
+ 1160,
// 1159 NA
// 1158 NA
- // 1157,
- // 1156,
+ 1157,
+ // 1156 NA
// 1155 NA
- // 1154,
+ // 1154 NA
// 1153,
// 1152 NA
// 1151,
@@ -144,19 +548,19 @@ static int included_patches[] = {
// 1147,
// 1146 NA
// 1145 NA
- // 1144 NA
- // 1143,
+ 1144,
+ 1143,
// 1142,
- // 1141,
+ 1141,
// 1140,
// 1139 NA
// 1138 NA
1137,
// 1136,
- // 1135 NA,
- // 1134 NA,
+ // 1135 NA
+ // 1134 NA
// 1133 NA
- // 1132,
+ 1132,
// 1131 NA
// 1130,
// 1129 NA
@@ -165,137 +569,137 @@ static int included_patches[] = {
// 1126,
// 1125 NA
// 1124 NA
- // 1123,
+ 1123,
// 1122 NA
// 1121,
- // 1120,
- // 1119,
- // 1118,
- // 1117,
- // 1116,
+ 1120,
+ 1119,
+ 1118,
+ 1117,
+ 1116,
// 1115 NA
- // 1114,
- // 1113,
- // 1112,
+ 1114,
+ 1113,
+ 1112,
// 1111,
- // 1110,
+ 1110,
// 1109 NA
// 1108,
- // 1107,
+ 1107,
// 1106 NA
- // 1105,
+ 1105,
// 1104 NA
// 1103 NA
- // 1102,
- // 1101,
+ 1102,
+ 1101,
// 1100 NA
// 1099 NA
// 1098 NA
// 1097,
- // 1096,
+ 1096,
// 1095 NA
// 1094,
- // 1093,
- // 1092,
- // 1091,
+ 1093,
+ 1092,
+ 1091,
// 1090,
1089,
1088,
1087,
- // 1086,
+ 1086,
1085,
1084,
- // 1083 NA,
- // 1082 NA,
+ // 1083 NA
+ // 1082 NA
1081,
- // 1080 NA,
+ // 1080 NA
// 1079,
- // 1078 NA,
- // 1077 NA,
+ // 1078 NA
+ // 1077 NA
1076,
- // 1075,
+ 1075,
// 1074 NA,
// 1073,
1072,
- // 1071,
- // 1070 NA,
- // 1069 NA,
+ 1071,
+ // 1070 NA
+ // 1069 NA
// 1068,
- // 1067 NA,
- // 1066 NA,
+ // 1067 NA
+ // 1066 NA
1065,
- // 1064,
- // 1063 NA,
- // 1062 NA,
- // 1061,
- // 1060 NA,
- // 1059,
+ 1064,
+ // 1063 NA
+ // 1062 NA
+ 1061,
+ // 1060 NA
+ 1059,
// 1058,
- // 1057,
+ 1057,
// 1056,
1055,
- // 1054,
- // 1053,
- // 1052,
+ 1054,
+ 1053,
+ 1052,
// 1051,
- // 1050,
- // 1049,
- // 1048,
- // 1047,
- // 1046,
- // 1045 NA,
- // 1044 NA,
- // 1043 NA,
- // 1042,
- // 1041,
- // 1040 NA,
+ 1050,
+ 1049,
+ 1048,
+ 1047,
+ 1046,
+ // 1045 NA
+ // 1044 NA
+ // 1043 NA
+ 1042,
+ 1041,
+ // 1040 NA
// 1039,
- // 1038 NA,
- // 1037,
- // 1036,
- // 1035,
- // 1034,
- // 1033 NA,
+ // 1038 NA
+ 1037,
+ 1036,
+ 1035,
+ 1034,
+ // 1033 NA
1032,
// 1031 NA,
- // 1030,
+ 1030,
1029,
- // 1028 NA,
+ // 1028 NA
1027,
- // 1026 NA,
- // 1025 NA,
- // 1024 NA,
- // 1023 NA,
- // 1022 NA,
- // 1021 NA,
- // 1020 NA,
- // 1019 NA,
- // 1018,
- // 1017,
- // 1016 NA,
- // 1015,
- // 1014 NA,
+ // 1026 NA
+ // 1025 NA
+ // 1024 NA
+ // 1023 NA
+ // 1022 NA
+ // 1021 NA
+ // 1020 NA
+ // 1019 NA
+ 1018,
+ 1017,
+ // 1016 NA
+ 1015,
+ // 1014 NA
1013,
- // 1012 NA,
- // 1011 NA,
- // 1010,
- // 1009 NA,
- // 1008 NA,
- // 1007,
- // 1006,
- // 1005,
+ // 1012 NA
+ // 1011 NA
+ // 1010 NA,
+ // 1009 NA
+ // 1008 NA
+ 1007,
+ 1006,
+ // 1005 NA,
// 1004 NA,
// 1003 NA,
// 1002 NA,
- // 1001,
- // 1000,
+ 1001,
+ 1000,
// 999 NA
- // 998,
+ 998,
// 997 NA
// 996 NA
// 995 NA
// 994 NA
- // 993,
+ // 993 NA
// 992 NA
991,
// 990 NA
@@ -304,22 +708,22 @@ static int included_patches[] = {
// 987 NA
// 986 NA
// 985 NA
- // 984,
- // 983,
+ 984,
+ // 983 NA
// 982 NA
981,
980,
// 979 NA
978,
- // 977,
+ 977,
// 976 NA
975,
- // 974,
- // 973,
+ 974,
+ 973,
972,
// 971 NA
// 970 NA
- // 969,
+ // 969 NA
// 968 NA
// 967 NA
// 966 NA
@@ -327,176 +731,176 @@ static int included_patches[] = {
// 964 NA
963,
// 962 NA
- // 961,
+ 961,
// 960 NA
// 959 NA
- // 958,
- // 957,
- // 956,
+ 958,
+ 957,
+ // 956 NA
955,
// 954 NA
953,
- // 952,
- // 951,
+ 952,
+ 951,
950,
949,
// 948 NA
- // 947,
+ // 947 NA
946,
945,
944,
- // 943,
- // 942,
- // 941,
+ // 943 NA
+ 942,
+ 941,
// 940 NA
- // 939,
+ 939,
// 938 NA
- // 937,
- // 936,
- // 935,
+ 937,
+ 936,
+ // 935 NA
// 934 NA
- // 933,
- // 932,
- // 931,
+ 933,
+ 932,
+ // 931 NA
// 930 NA
- // 929,
+ 929,
// 928 NA
// 927 NA
- // 926,
- // 925,
+ 926,
+ 925,
// 924 NA
// 923 NA
- // 922,
+ 922,
// 921 NA
// 920 NA
// 919 NA
// 918 NA
// 917 NA
916,
- // 915,
- // 914,
+ 915,
+ // 914 NA
// 913 NA
- // 912,
+ 912,
// 911 NA
// 910 NA
- // 909,
+ // 909 NA
// 908 NA
// 907 NA
// 906 NA
- // 905,
- // 904,
- // 903,
+ // 905 NA
+ // 904 NA
+ 903,
// 902 NA
- // 901,
+ 901,
// 900 NA
// 899 NA
898,
- // 897,
- // 896,
- // 895,
+ // 897 NA
+ 896,
+ 895,
// 894 NA
- // 893,
- // 892,
- // 891,
+ 893,
+ // 892 NA
+ 891,
// 890 NA
- // 889,
- // 888,
- // 887,
+ 889,
+ 888,
+ 887,
// 886 NA
- // 885,
+ 885,
// 884 NA
- // 883,
- // 882,
- // 881,
+ 883,
+ 882,
+ 881,
// 880 NA
- // 879,
- // 878,
- // 877,
+ 879,
+ 878,
+ 877,
// 876 NA
// 875 NA
// 874 NA
- // 873,
+ // 873 NA
// 872 NA
- // 871,
- // 870,
+ 871,
+ 870,
// 869 NA
- // 868,
+ 868,
// 867 NA
- // 866,
- // 865,
+ // 866 NA
+ // 865 NA
// 864 NA
// 863 NA
// 862 NA
// 861 NA
- // 860,
- // 859,
+ // 860 NA
+ 859,
858,
- // 857,
- // 856,
+ 857,
+ 856,
// 855 NA
// 854 NA
- // 853,
+ 853,
// 852 NA
// 851 NA
// 850 NA
849,
848,
- // 847,
+ 847,
// 846 NA
- // 845,
- // 844,
- // 843,
+ 845,
+ 844,
+ 843,
// 842 NA
// 841 NA
// 840 NA
- // 839,
- // 838,
+ // 839 NA
+ // 838 NA
// 837 NA
836,
- // 835,
- // 834,
- // 833,
- // 832,
- // 831,
- // 830,
+ 835,
+ 834,
+ 833,
+ 832,
+ 831,
+ 830,
// 829 NA
- // 828,
- // 827,
+ 828,
+ // 827 NA
826,
- // 825,
+ 825,
// 824 NA
823,
- // 822,
- // 821,
- // 820,
- // 819,
- // 818,
- // 817,
- // 816,
- // 815,
- // 814,
+ 822,
+ // 821 NA
+ 820,
+ 819,
+ 818,
+ 817,
+ 816,
+ 815,
+ 814,
813,
- // 812,
- // 811,
- // 810,
+ // 812 NA
+ 811,
+ 810,
809,
// 808 NA
807,
806,
- // 805,
- // 804,
+ 805,
+ // 804 NA
803,
802,
- // 801,
- // 800,
+ 801,
+ 800,
799,
- // 798,
- // 797,
+ 798,
+ // 797 NA
// 796 NA
795,
// 794 NA
793,
- // 792,
+ 792,
791,
790,
789,
@@ -517,53 +921,53 @@ static int included_patches[] = {
774,
773,
// 772 NA
- // 771,
+ 771,
// 770 NA
- // 769,
- // 768,
- // 767,
+ 769,
+ 768,
+ // 767 NA
// 766 NA
765,
764,
// 763 NA
// 762 NA
// 761 NA
- // 760,
+ 760,
// 759 NA
- // 758,
+ 758,
// 757 NA
// 756 NA
- // 755,
+ 755,
754,
753,
- // 752,
+ // 752 NA
// 751 NA
// 750 NA
- // 749,
+ 749,
748,
747,
746,
745,
// 744 NA
- // 743,
- // 742,
+ 743,
+ 742,
741,
740,
739,
// 738 NA
- // 737,
+ 737,
736,
- // 735,
- // 734,
- // 733,
- // 732,
+ // 735 NA
+ 734,
+ // 733 NA
+ 732,
// 731 NA
// 730 NA
729,
// 728 NA
// 727 NA
// 726 NA
- // 725,
+ // 725 NA
// 724 NA
723,
722,
@@ -571,7 +975,7 @@ static int included_patches[] = {
// 720 NA
719,
718,
- // 717,
+ 717,
716,
715,
714,
@@ -580,7 +984,7 @@ static int included_patches[] = {
711,
710,
709,
- // 708,
+ 708,
707,
706,
// 705 NA
@@ -604,7 +1008,7 @@ static int included_patches[] = {
// 687 NA
686,
685,
- // 684,
+ // 684 NA
// 683 NA
682,
// 681 NA
@@ -616,7 +1020,7 @@ static int included_patches[] = {
675,
// 674 NA
673,
- // 672,
+ 672,
671,
670,
// 669 NA
@@ -675,7 +1079,7 @@ static int included_patches[] = {
616,
615,
614,
- // 613,
+ 613,
612,
// 611 NA
// 610 NA
@@ -1044,13 +1448,13 @@ static int included_patches[] = {
247,
// 246 NA
245,
- // 244,
+ // 244 NA
243,
242,
241,
240,
239,
- // 238,
+ // 238 NA
237,
236,
235,
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 5f9785a9a9..165a44a148 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -27,12 +27,6 @@ Error: configure did not run properly.Check auto/config.log.
# endif
#endif
-
-/* Can't use "PACKAGE" here, conflicts with a Perl include file. */
-#ifndef VIMPACKAGE
-# define VIMPACKAGE "vim"
-#endif
-
#include "nvim/os/os_defs.h" /* bring lots of system header files */
/// length of a buffer to store a number in ASCII (64 bits binary + NUL)
@@ -207,15 +201,6 @@ enum {
#define MAYBE 2 /* sometimes used for a variant on TRUE */
-/*
- * Motion types, used for operators and for yank/delete registers.
- */
-#define MCHAR 0 /* character-wise movement/register */
-#define MLINE 1 /* line-wise movement/register */
-#define MBLOCK 2 /* block-wise register */
-
-#define MAUTO 0xff /* Decide between MLINE/MCHAR */
-
#define STATUS_HEIGHT 1 /* height of a status line under a window */
#define QF_WINHEIGHT 10 /* default height for quickfix window */
@@ -277,26 +262,11 @@ enum {
# define vim_strpbrk(s, cs) (char_u *)strpbrk((char *)(s), (char *)(cs))
-#define MSG(s) msg((char_u *)(s))
-#define MSG_ATTR(s, attr) msg_attr((char_u *)(s), (attr))
-#define EMSG(s) emsg((char_u *)(s))
-#define EMSG2(s, p) emsg2((char_u *)(s), (char_u *)(p))
-#define EMSG3(s, p, q) emsg3((char_u *)(s), (char_u *)(p), \
- (char_u *)(q))
-#define EMSGN(s, n) emsgn((char_u *)(s), (int64_t)(n))
-#define EMSGU(s, n) emsgu((char_u *)(s), (uint64_t)(n))
-#define OUT_STR(s) out_str((char_u *)(s))
-#define OUT_STR_NF(s) out_str_nf((char_u *)(s))
-#define MSG_PUTS(s) msg_puts((char_u *)(s))
-#define MSG_PUTS_ATTR(s, a) msg_puts_attr((char_u *)(s), (a))
-#define MSG_PUTS_TITLE(s) msg_puts_title((char_u *)(s))
-#define MSG_PUTS_LONG(s) msg_puts_long_attr((char_u *)(s), 0)
-#define MSG_PUTS_LONG_ATTR(s, a) msg_puts_long_attr((char_u *)(s), (a))
-
-/* Prefer using emsg3(), because perror() may send the output to the wrong
- * destination and mess up the screen. */
-#define PERROR(msg) \
- (void) emsg3((char_u *) "%s: %s", (char_u *)msg, (char_u *)strerror(errno))
+#include "nvim/message.h"
+
+// Prefer using emsgf(), because perror() may send the output to the wrong
+// destination and mess up the screen.
+#define PERROR(msg) (void) emsgf("%s: %s", msg, strerror(errno))
#define SHOWCMD_COLS 10 /* columns needed by shown command */
#define STL_MAX_ITEM 80 /* max nr of %<flag> in statusline */
diff --git a/src/nvim/window.c b/src/nvim/window.c
index e84d8df36b..bea55c465f 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -97,7 +97,7 @@ do_window (
* don't replicate the quickfix buffer. */
if (bt_quickfix(curbuf))
goto newwindow;
- win_split((int)Prenum, 0);
+ (void)win_split((int)Prenum, 0);
break;
/* split current window in two parts, vertically */
@@ -108,7 +108,7 @@ do_window (
* don't replicate the quickfix buffer. */
if (bt_quickfix(curbuf))
goto newwindow;
- win_split((int)Prenum, WSP_VERT);
+ (void)win_split((int)Prenum, WSP_VERT);
break;
/* split current window and edit alternate file */
@@ -248,7 +248,7 @@ newwindow:
/* First create a new tab with the window, then go back to
* the old tab and close the window there. */
wp = curwin;
- if (win_new_tabpage((int)Prenum) == OK
+ if (win_new_tabpage((int)Prenum, NULL) == OK
&& valid_tabpage(oldtab)) {
newtab = curtab;
goto_tabpage_tp(oldtab, TRUE, TRUE);
@@ -2948,18 +2948,19 @@ void free_tabpage(tabpage_T *tp)
unref_var_dict(tp->tp_vars);
-
+ xfree(tp->localdir); // Free tab-local working directory
xfree(tp);
}
-/*
- * Create a new Tab page with one window.
- * It will edit the current buffer, like after ":split".
- * When "after" is 0 put it just after the current Tab page.
- * Otherwise put it just before tab page "after".
- * Return FAIL or OK.
- */
-int win_new_tabpage(int after)
+/// Create a new tabpage with one window.
+///
+/// It will edit the current buffer, like after :split.
+///
+/// @param after Put new tabpage after tabpage "after", or after the current
+/// tabpage in case of 0.
+/// @param filename Will be passed to apply_autocmds().
+/// @return Was the new tabpage created successfully? FAIL or OK.
+int win_new_tabpage(int after, char_u *filename)
{
tabpage_T *tp = curtab;
tabpage_T *newtp;
@@ -2999,10 +3000,12 @@ int win_new_tabpage(int after)
newtp->tp_topframe = topframe;
last_status(FALSE);
-
redraw_all_later(CLEAR);
- apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
- apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
+
+ apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf);
+ apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf);
+ apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf);
+
return OK;
}
@@ -3023,7 +3026,7 @@ int may_open_tabpage(void)
if (n != 0) {
cmdmod.tab = 0; /* reset it to avoid doing it twice */
postponed_split_tab = 0;
- return win_new_tabpage(n);
+ return win_new_tabpage(n, NULL);
}
return FAIL;
}
@@ -3047,9 +3050,11 @@ int make_tabpages(int maxcount)
*/
block_autocmds();
- for (todo = count - 1; todo > 0; --todo)
- if (win_new_tabpage(0) == FAIL)
+ for (todo = count - 1; todo > 0; --todo) {
+ if (win_new_tabpage(0, NULL) == FAIL) {
break;
+ }
+ }
unblock_autocmds();
@@ -3555,18 +3560,24 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int tri
curwin->w_cursor.coladd = 0;
changed_line_abv_curs(); /* assume cursor position needs updating */
- if (curwin->w_localdir != NULL) {
- /* Window has a local directory: Save current directory as global
- * directory (unless that was done already) and change to the local
- * directory. */
+ // The new directory is either the local directory of the window, of the tab
+ // or NULL.
+ char_u *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->localdir;
+
+ if (new_dir) {
+ // Window/tab has a local directory: Save current directory as global
+ // directory (unless that was done already) and change to the local
+ // directory.
if (globaldir == NULL) {
char_u cwd[MAXPATHL];
- if (os_dirname(cwd, MAXPATHL) == OK)
+ if (os_dirname(cwd, MAXPATHL) == OK) {
globaldir = vim_strsave(cwd);
+ }
+ }
+ if (os_chdir((char *)new_dir) == 0) {
+ shorten_fnames(true);
}
- if (os_chdir((char *)curwin->w_localdir) == 0)
- shorten_fnames(TRUE);
} else if (globaldir != NULL) {
/* Window doesn't have a local directory and we are not in the global
* directory: Change to the global directory. */
@@ -4575,10 +4586,19 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
}
assert(fr);
- if (room < offset) /* Not enough room */
- offset = room; /* Move as far as we can */
- if (offset <= 0) /* No room at all, quit. */
+ // Not enough room
+ if (room < offset) {
+ offset = room; // Move as far as we can
+ }
+
+ // No room at all, quit.
+ if (offset <= 0) {
return;
+ }
+
+ if (fr == NULL) {
+ return; // Safety check, should not happen.
+ }
/* grow frame fr by offset lines */
frame_new_width(fr, fr->fr_width + offset, left, FALSE);
@@ -4610,10 +4630,8 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
#define FRACTION_MULT 16384L
-/*
- * Set wp->w_fraction for the current w_wrow and w_height.
- */
-static void set_fraction(win_T *wp)
+// Set wp->w_fraction for the current w_wrow and w_height.
+void set_fraction(win_T *wp)
{
wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height / 2)
/ (long)wp->w_height;
@@ -5342,14 +5360,14 @@ void restore_buffer(buf_T *save_curbuf)
}
-/*
- * Add match to the match list of window 'wp'. The pattern 'pat' will be
- * highlighted with the group 'grp' with priority 'prio'.
- * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
- * If no particular ID is desired, -1 must be specified for 'id'.
- * Return ID of added match, -1 on failure.
- */
-int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id, list_T *pos_list)
+// Add match to the match list of window 'wp'. The pattern 'pat' will be
+// highlighted with the group 'grp' with priority 'prio'.
+// Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
+// If no particular ID is desired, -1 must be specified for 'id'.
+// Return ID of added match, -1 on failure.
+int match_add(win_T *wp, char_u *grp, char_u *pat,
+ int prio, int id, list_T *pos_list,
+ char_u *conceal_char)
{
matchitem_T *cur;
matchitem_T *prev;
@@ -5405,6 +5423,10 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id, list_T *pos
m->match.regprog = regprog;
m->match.rmm_ic = FALSE;
m->match.rmm_maxcol = 0;
+ m->conceal_char = 0;
+ if (conceal_char != NULL) {
+ m->conceal_char = (*mb_ptr2char)(conceal_char);
+ }
// Set up position matches
if (pos_list != NULL)