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.txt57
-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/vim.c34
-rw-r--r--src/nvim/api/window.c33
-rw-r--r--src/nvim/arabic.c18
-rw-r--r--src/nvim/ascii.h31
-rw-r--r--src/nvim/assert.h101
-rw-r--r--src/nvim/auevents.lua1
-rw-r--r--src/nvim/buffer.c666
-rw-r--r--src/nvim/buffer.h1
-rw-r--r--src/nvim/buffer_defs.h252
-rw-r--r--src/nvim/bufhl_defs.h25
-rw-r--r--src/nvim/charset.c395
-rw-r--r--src/nvim/diff.c265
-rw-r--r--src/nvim/edit.c1227
-rw-r--r--src/nvim/edit.h6
-rw-r--r--src/nvim/eval.c4395
-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.h95
-rw-r--r--src/nvim/event/libuv_process.c3
-rw-r--r--src/nvim/event/process.c29
-rw-r--r--src/nvim/event/process.h5
-rw-r--r--src/nvim/ex_cmds.c549
-rw-r--r--src/nvim/ex_cmds.lua47
-rw-r--r--src/nvim/ex_cmds2.c295
-rw-r--r--src/nvim/ex_cmds_defs.h1
-rw-r--r--src/nvim/ex_docmd.c587
-rw-r--r--src/nvim/ex_docmd.h14
-rw-r--r--src/nvim/ex_eval.c107
-rw-r--r--src/nvim/ex_getln.c233
-rw-r--r--src/nvim/ex_getln.h31
-rw-r--r--src/nvim/farsi.c31
-rw-r--r--src/nvim/file_search.c94
-rw-r--r--src/nvim/fileio.c901
-rw-r--r--src/nvim/fold.c39
-rw-r--r--src/nvim/garray.c24
-rw-r--r--src/nvim/getchar.c235
-rw-r--r--src/nvim/globals.h143
-rw-r--r--src/nvim/hardcopy.c34
-rw-r--r--src/nvim/if_cscope.c190
-rw-r--r--src/nvim/if_cscope_defs.h9
-rw-r--r--src/nvim/indent.c2
-rw-r--r--src/nvim/indent_c.c831
-rw-r--r--src/nvim/keymap.c283
-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/log.c4
-rw-r--r--src/nvim/macros.h11
-rw-r--r--src/nvim/main.c80
-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.c59
-rw-r--r--src/nvim/memfile.c173
-rw-r--r--src/nvim/memline.c21
-rw-r--r--src/nvim/memline_defs.h2
-rw-r--r--src/nvim/menu.c10
-rw-r--r--src/nvim/message.c335
-rw-r--r--src/nvim/message.h51
-rw-r--r--src/nvim/misc1.c559
-rw-r--r--src/nvim/misc1.h1
-rw-r--r--src/nvim/misc2.c5
-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/defs.h18
-rw-r--r--src/nvim/msgpack_rpc/helpers.c11
-rw-r--r--src/nvim/msgpack_rpc/server.c4
-rw-r--r--src/nvim/normal.c800
-rw-r--r--src/nvim/normal.h52
-rw-r--r--src/nvim/ops.c1548
-rw-r--r--src/nvim/ops.h72
-rw-r--r--src/nvim/option.c596
-rw-r--r--src/nvim/option_defs.h301
-rw-r--r--src/nvim/options.lua196
-rw-r--r--src/nvim/os/dl.c2
-rw-r--r--src/nvim/os/env.c162
-rw-r--r--src/nvim/os/fs.c163
-rw-r--r--src/nvim/os/fs_defs.h6
-rw-r--r--src/nvim/os/input.c22
-rw-r--r--src/nvim/os/mem.c2
-rw-r--r--src/nvim/os/os_defs.h38
-rw-r--r--src/nvim/os/shell.c28
-rw-r--r--src/nvim/os/stdpaths.c15
-rw-r--r--src/nvim/os/time.c4
-rw-r--r--src/nvim/os/unix_defs.h12
-rw-r--r--src/nvim/os/users.c6
-rw-r--r--src/nvim/os/win_defs.h57
-rw-r--r--src/nvim/os_unix.c38
-rw-r--r--src/nvim/os_unix.h6
-rw-r--r--src/nvim/path.c458
-rw-r--r--src/nvim/path.h5
-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.po548
-rw-r--r--src/nvim/popupmnu.c8
-rw-r--r--src/nvim/pos.h6
-rw-r--r--src/nvim/quickfix.c614
-rw-r--r--src/nvim/regexp.c136
-rw-r--r--src/nvim/regexp_nfa.c559
-rw-r--r--src/nvim/screen.c566
-rw-r--r--src/nvim/screen.h23
-rw-r--r--src/nvim/search.c445
-rw-r--r--src/nvim/search.h27
-rw-r--r--src/nvim/shada.c301
-rw-r--r--src/nvim/spell.c72
-rw-r--r--src/nvim/strings.c56
-rw-r--r--src/nvim/syntax.c143
-rw-r--r--src/nvim/tag.c108
-rw-r--r--src/nvim/tempfile.c138
-rw-r--r--src/nvim/tempfile.h8
-rw-r--r--src/nvim/terminal.c110
-rw-r--r--src/nvim/testdir/Makefile83
-rw-r--r--src/nvim/testdir/runtest.vim128
-rw-r--r--src/nvim/testdir/test11.in84
-rw-r--r--src/nvim/testdir/test11.ok61
-rw-r--r--src/nvim/testdir/test13.in6
-rw-r--r--src/nvim/testdir/test13.ok1
-rw-r--r--src/nvim/testdir/test17.in26
-rw-r--r--src/nvim/testdir/test30.in58
-rw-r--r--src/nvim/testdir/test30.ok9
-rw-r--r--src/nvim/testdir/test36.in105
-rw-r--r--src/nvim/testdir/test36.ok96
-rw-r--r--src/nvim/testdir/test39.in106
-rw-r--r--src/nvim/testdir/test39.okbin585 -> 0 bytes
-rw-r--r--src/nvim/testdir/test45.in80
-rw-r--r--src/nvim/testdir/test45.ok18
-rw-r--r--src/nvim/testdir/test47.in45
-rw-r--r--src/nvim/testdir/test47.ok40
-rw-r--r--src/nvim/testdir/test49.in4
-rw-r--r--src/nvim/testdir/test49.ok15
-rw-r--r--src/nvim/testdir/test49.vim875
-rw-r--r--src/nvim/testdir/test53.in4
-rw-r--r--src/nvim/testdir/test53.ok1
-rw-r--r--src/nvim/testdir/test55.in171
-rw-r--r--src/nvim/testdir/test55.ok61
-rw-r--r--src/nvim/testdir/test68.in131
-rw-r--r--src/nvim/testdir/test68.ok77
-rw-r--r--src/nvim/testdir/test83-tags22
-rw-r--r--src/nvim/testdir/test83-tags3102
-rw-r--r--src/nvim/testdir/test83.in75
-rw-r--r--src/nvim/testdir/test83.ok4
-rw-r--r--src/nvim/testdir/test88.in88
-rw-r--r--src/nvim/testdir/test88.ok24
-rw-r--r--src/nvim/testdir/test_alot.vim3
-rw-r--r--src/nvim/testdir/test_breakindent.in2
-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_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_eval.in223
-rw-r--r--src/nvim/testdir/test_eval.okbin11106 -> 0 bytes
-rw-r--r--src/nvim/testdir/test_eval_func.vim12
-rw-r--r--src/nvim/testdir/test_listlbr.in49
-rw-r--r--src/nvim/testdir/test_listlbr.ok24
-rw-r--r--src/nvim/testdir/test_viml.vim930
-rw-r--r--src/nvim/tui/input.c12
-rw-r--r--src/nvim/tui/tui.c22
-rw-r--r--src/nvim/ui.c27
-rw-r--r--src/nvim/undo.c119
-rw-r--r--src/nvim/version.c1051
-rw-r--r--src/nvim/vim.h64
-rw-r--r--src/nvim/window.c456
-rw-r--r--src/nvim/window.h1
177 files changed, 18358 insertions, 12950 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 1038895b86..6b2ce08d36 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,33 @@ 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
+ )
+ 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 +81,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
@@ -108,6 +111,10 @@ if(NOT MSVC)
${CONV_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion")
endif()
+if(DEFINED MIN_LOG_LEVEL)
+ add_definitions(-DMIN_LOG_LEVEL=${MIN_LOG_LEVEL})
+endif()
+
get_directory_property(gen_cdefs COMPILE_DEFINITIONS)
foreach(gen_cdef ${gen_cdefs} DO_NOT_DEFINE_EMPTY_ATTRIBUTES)
if(NOT "${gen_cdef}" MATCHES "INCLUDE_GENERATED_DECLARATIONS")
@@ -252,8 +259,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/api/vim.c b/src/nvim/api/vim.c
index 9279f6b469..46ac3c9022 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -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
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/ascii.h b/src/nvim/ascii.h
index 2b3e94d5a0..44ff540b40 100644
--- a/src/nvim/ascii.h
+++ b/src/nvim/ascii.h
@@ -85,10 +85,25 @@
# define PATHSEPSTR "/"
#endif
-static inline bool ascii_iswhite(int) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST;
-static inline bool ascii_isdigit(int) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST;
-static inline bool ascii_isxdigit(int) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST;
-static inline bool ascii_isspace(int) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST;
+static inline bool ascii_iswhite(int)
+ REAL_FATTR_CONST
+ REAL_FATTR_ALWAYS_INLINE;
+
+static inline bool ascii_isdigit(int)
+ REAL_FATTR_CONST
+ REAL_FATTR_ALWAYS_INLINE;
+
+static inline bool ascii_isxdigit(int)
+ REAL_FATTR_CONST
+ REAL_FATTR_ALWAYS_INLINE;
+
+static inline bool ascii_isbdigit(int)
+ REAL_FATTR_CONST
+ REAL_FATTR_ALWAYS_INLINE;
+
+static inline bool ascii_isspace(int)
+ REAL_FATTR_CONST
+ REAL_FATTR_ALWAYS_INLINE;
/// Checks if `c` is a space or tab character.
///
@@ -122,6 +137,14 @@ static inline bool ascii_isxdigit(int c)
|| (c >= 'A' && c <= 'F');
}
+/// Checks if `c` is a binary digit, that is, 0-1.
+///
+/// @see {ascii_isdigit}
+static inline bool ascii_isbdigit(int c)
+{
+ return (c == '0' || c == '1');
+}
+
/// Checks if `c` is a white-space character, that is,
/// one of \f, \n, \r, \t, \v.
///
diff --git a/src/nvim/assert.h b/src/nvim/assert.h
index 3a900aca65..2c43777858 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,74 @@
// 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
+# define STATIC_ASSERT_PRAGMA_START \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-pedantic\"") \
+
+# 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 fae8e9ecd0..72716daf0e 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -22,6 +22,7 @@
#include "nvim/api/private/handle.h"
#include "nvim/ascii.h"
+#include "nvim/assert.h"
#include "nvim/vim.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
@@ -140,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;
@@ -256,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;
}
/*
@@ -400,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))
@@ -579,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;
}
@@ -918,13 +925,15 @@ do_buffer (
if (buf == NULL) { /* could not find it */
if (start == DOBUF_FIRST) {
- /* don't warn when deleting */
- if (!unload)
- EMSGN(_("E86: Buffer %" PRId64 " does not exist"), count);
- } else if (dir == FORWARD)
+ // don't warn when deleting
+ if (!unload) {
+ EMSGN(_(e_nobufnr), count);
+ }
+ } else if (dir == FORWARD) {
EMSG(_("E87: Cannot go beyond last buffer"));
- else
+ } else {
EMSG(_("E88: Cannot go before first buffer"));
+ }
return FAIL;
}
@@ -1284,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);
}
}
}
@@ -1329,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) {
@@ -1414,7 +1424,6 @@ buflist_new (
return NULL;
if (aborting()) /* autocmds may abort script processing */
return NULL;
- /* buf->b_nwindows = 0; why was this here? */
free_buffer_stuff(buf, FALSE); /* delete local variables et al. */
/* Init the options. */
@@ -1475,6 +1484,9 @@ buflist_new (
fmarks_check_names(buf); /* check file marks for this file */
buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE; /* init 'buflisted' */
if (!(flags & BLN_DUMMY)) {
+ // Tricky: these autocommands may change the buffer list. They could also
+ // split the window with re-using the one empty buffer. This may result in
+ // unexpectedly losing the empty buffer.
apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf);
if (!buf_valid(buf)) {
return NULL;
@@ -1541,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);
@@ -1596,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);
}
}
@@ -1709,18 +1729,15 @@ static buf_T *buflist_findname_file_id(char_u *ffname, FileID *file_id,
return NULL;
}
-/*
- * Find file in buffer list by a regexp pattern.
- * Return fnum of the found buffer.
- * Return < 0 for error.
- */
-int
-buflist_findpat (
- char_u *pattern,
- char_u *pattern_end, /* pointer to first char after pattern */
- int unlisted, /* find unlisted buffers */
- int diffmode, /* find diff-mode buffers only */
- int curtab_only /* find buffers in current tab only */
+/// Find file in buffer list by a regexp pattern.
+/// Return fnum of the found buffer.
+/// Return < 0 for error.
+int buflist_findpat(
+ const char_u *pattern,
+ const char_u *pattern_end, // pointer to first char after pattern
+ int unlisted, // find unlisted buffers
+ int diffmode, // find diff-mode buffers only
+ int curtab_only // find buffers in current tab only
)
{
int match = -1;
@@ -1732,12 +1749,15 @@ 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:
@@ -2042,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;
}
@@ -2176,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 {
@@ -2408,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.
@@ -2470,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);
}
/*
@@ -2757,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;
}
/*
@@ -2825,7 +2863,7 @@ typedef enum {
/// @param fillchar Character to use when filling empty space in the statusline
/// @param maxwidth The maximum width to make the statusline
/// @param hltab HL attributes (can be NULL)
-/// @param tabtab tab page nrs (can be NULL)
+/// @param tabtab Tab clicks definition (can be NULL).
///
/// @return The final width of the statusline
int build_stl_str_hl(
@@ -2837,13 +2875,15 @@ int build_stl_str_hl(
int fillchar,
int maxwidth,
struct stl_hlrec *hltab,
- struct stl_hlrec *tabtab
+ StlClickRecord *tabtab
)
{
int groupitem[STL_MAX_ITEM];
struct stl_item {
// Where the item starts in the status line output buffer
- char_u *start;
+ char_u *start;
+ // Function to run for ClickFunc items.
+ char *cmd;
// The minimum width of the item
int minwid;
// The maximum width of the item
@@ -2855,10 +2895,10 @@ int build_stl_str_hl(
Middle,
Highlight,
TabPage,
+ ClickFunc,
Trunc
- } type;
- } item[STL_MAX_ITEM];
-
+ } type;
+ } item[STL_MAX_ITEM];
#define TMPLEN 70
char_u tmp[TMPLEN];
char_u *usefmt = fmt;
@@ -2999,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;
}
@@ -3163,6 +3203,24 @@ int build_stl_str_hl(
continue;
}
+ if (*fmt_p == STL_CLICK_FUNC) {
+ fmt_p++;
+ char *t = (char *) fmt_p;
+ while (*fmt_p != STL_CLICK_FUNC && *fmt_p) {
+ fmt_p++;
+ }
+ if (*fmt_p != STL_CLICK_FUNC) {
+ break;
+ }
+ item[curitem].type = ClickFunc;
+ item[curitem].start = out_p;
+ item[curitem].cmd = xmemdupz(t, (size_t) (((char *) fmt_p - t)));
+ item[curitem].minwid = minwid;
+ fmt_p++;
+ curitem++;
+ continue;
+ }
+
// Denotes the end of the minwid
// the maxwid may follow immediately after
if (*fmt_p == '.') {
@@ -3280,6 +3338,7 @@ int build_stl_str_hl(
}
break;
}
+
case STL_LINE:
num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
? 0L : (long)(wp->w_cursor.lnum);
@@ -3820,16 +3879,37 @@ int build_stl_str_hl(
// Store the info about tab pages labels.
if (tabtab != NULL) {
- struct stl_hlrec *sp = tabtab;
+ StlClickRecord *cur_tab_rec = tabtab;
for (long l = 0; l < itemcnt; l++) {
if (item[l].type == TabPage) {
- sp->start = item[l].start;
- sp->userhl = item[l].minwid;
- sp++;
+ cur_tab_rec->start = (char *) item[l].start;
+ if (item[l].minwid == 0) {
+ cur_tab_rec->def.type = kStlClickDisabled;
+ cur_tab_rec->def.tabnr = 0;
+ } else {
+ int tabnr = item[l].minwid;
+ if (item[l].minwid > 0) {
+ cur_tab_rec->def.type = kStlClickTabSwitch;
+ } else {
+ cur_tab_rec->def.type = kStlClickTabClose;
+ tabnr = -tabnr;
+ }
+ cur_tab_rec->def.tabnr = tabnr;
+ }
+ cur_tab_rec->def.func = NULL;
+ cur_tab_rec++;
+ } else if (item[l].type == ClickFunc) {
+ cur_tab_rec->start = (char *) item[l].start;
+ cur_tab_rec->def.type = kStlClickFuncRun;
+ cur_tab_rec->def.tabnr = item[l].minwid;
+ cur_tab_rec->def.func = item[l].cmd;
+ cur_tab_rec++;
}
}
- sp->start = NULL;
- sp->userhl = 0;
+ cur_tab_rec->start = NULL;
+ cur_tab_rec->def.type = kStlClickDisabled;
+ cur_tab_rec->def.tabnr = 0;
+ cur_tab_rec->def.func = NULL;
}
return width;
@@ -3851,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);
@@ -3862,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) {
@@ -3889,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;
}
/*
@@ -4525,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;
@@ -4811,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.
*/
@@ -4825,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.h b/src/nvim/buffer.h
index 49025d3925..d51a2f7dae 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -4,6 +4,7 @@
#include "nvim/window.h"
#include "nvim/pos.h" // for linenr_T
#include "nvim/ex_cmds_defs.h" // for exarg_T
+#include "nvim/screen.h" // for StlClickRecord
// Values for buflist_getfile()
enum getf_values {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 3eabb7ee43..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,84 +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_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 */
@@ -750,6 +761,10 @@ struct file_buffer {
Terminal *terminal; // Terminal instance associated with the 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
};
/*
@@ -803,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
};
/*
@@ -901,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
};
/*
@@ -939,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 b93eafbf60..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.
+/// Check that "c" is a printable character.
+/// Assume characters above 0x100 are printable for double-byte encodings.
///
-/// @param c
-///
-/// @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".
-///
-/// @param wp
-/// @param vcol
+/// Check that virtual column "vcol" is in the rightmost column of window "wp".
///
-/// @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);
@@ -1454,6 +1461,24 @@ char_u* skipdigits(char_u *q)
return p;
}
+/// skip over binary digits
+///
+/// @param q pointer to string
+///
+/// @return Pointer to the character after the skipped digits.
+const char* skipbin(const char *q)
+ FUNC_ATTR_PURE
+ FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_RET
+{
+ const char *p = q;
+ while (ascii_isbdigit(*p)) {
+ // skip to next non-digit
+ p++;
+ }
+ return p;
+}
+
/// skip over digits and hex characters
///
/// @param q
@@ -1485,6 +1510,24 @@ char_u* skiptodigit(char_u *q)
return p;
}
+/// skip to binary character (or NUL after the string)
+///
+/// @param q pointer to string
+///
+/// @return Pointer to the binary character or (NUL after the string).
+const char* skiptobin(const char *q)
+ FUNC_ATTR_PURE
+ FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_RET
+{
+ const char *p = q;
+ while (*p != NUL && !ascii_isbdigit(*p)) {
+ // skip to next digit
+ p++;
+ }
+ return p;
+}
+
/// skip to hex character (or NUL after the string)
///
/// @param q
@@ -1535,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) {
@@ -1548,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) {
@@ -1562,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) {
@@ -1575,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) {
@@ -1602,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
@@ -1629,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
@@ -1708,79 +1759,90 @@ 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';
}
/// Convert a string into a long and/or unsigned long, taking care of
-/// hexadecimal and octal numbers. Accepts a '-' sign.
-/// If "hexp" is not NULL, returns a flag to indicate the type of the number:
+/// hexadecimal, octal and binary numbers. Accepts a '-' sign.
+/// If "prep" is not NULL, returns a flag to indicate the type of the number:
/// 0 decimal
/// '0' octal
+/// 'B' bin
+/// 'b' bin
/// 'X' hex
/// 'x' hex
/// If "len" is not NULL, the length of the number in characters is returned.
/// If "nptr" is not NULL, the signed result is returned in it.
/// If "unptr" is not NULL, the unsigned result is returned in it.
-/// If "dooct" is non-zero recognize octal numbers, when > 1 always assume
-/// octal number.
-/// If "dohex" is non-zero recognize hex numbers, when > 1 always assume
-/// hex number.
+/// If "what" contains STR2NR_BIN recognize binary numbers.
+/// If "what" contains STR2NR_OCT recognize octal numbers.
+/// If "what" contains STR2NR_HEX recognize hex numbers.
+/// If "what" contains STR2NR_FORCE always assume bin/oct/hex.
+/// If maxlen > 0, check at a maximum maxlen chars.
///
/// @param start
-/// @param hexp Returns type of number 0 = decimal, 'x' or 'X' is hex,
-// '0' = octal
+/// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex,
+/// '0' = octal, 'b' or 'B' is bin
/// @param len Returns the detected length of number.
-/// @param dooct recognize octal number
-/// @param dohex recognize hex number
+/// @param what Recognizes what number passed.
/// @param nptr Returns the signed result.
/// @param unptr Returns the unsigned result.
-void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex,
- long *nptr, unsigned long *unptr)
+/// @param maxlen Max length of string to check.
+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;
- int hex = 0; // default is decimal
- int negative = FALSE;
+ const char_u *ptr = start;
+ int pre = 0; // default is decimal
+ bool negative = false;
unsigned long un = 0;
- int n;
if (ptr[0] == '-') {
- negative = TRUE;
- ++ptr;
+ negative = true;
+ ptr++;
}
- // Recognize hex and octal.
- if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')) {
- hex = ptr[1];
+ // Recognize hex, octal and bin.
+ if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')
+ && (maxlen == 0 || maxlen > 1)) {
+ pre = ptr[1];
- if (dohex
- && ((hex == 'X') || (hex == 'x'))
- && ascii_isxdigit(ptr[2])) {
+ if ((what & STR2NR_HEX)
+ && ((pre == 'X') || (pre == 'x'))
+ && ascii_isxdigit(ptr[2])
+ && (maxlen == 0 || maxlen > 2)) {
// hexadecimal
ptr += 2;
+ } else if ((what & STR2NR_BIN)
+ && ((pre == 'B') || (pre == 'b'))
+ && ascii_isbdigit(ptr[2])
+ && (maxlen == 0 || maxlen > 2)) {
+ // binary
+ ptr += 2;
} else {
- // default is decimal
- hex = 0;
+ // decimal or octal, default is decimal
+ pre = 0;
- if (dooct) {
+ if (what & STR2NR_OCT) {
// Don't interpret "0", "08" or "0129" as octal.
- for (n = 1; ascii_isdigit(ptr[n]); ++n) {
+ for (int n = 1; ascii_isdigit(ptr[n]); ++n) {
if (ptr[n] > '7') {
// can't be octal
- hex = 0;
+ pre = 0;
break;
}
-
if (ptr[n] >= '0') {
// assume octal
- hex = '0';
+ pre = '0';
+ }
+ if (n == maxlen) {
+ break;
}
}
}
@@ -1788,28 +1850,54 @@ void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex,
}
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
- if ((hex == '0') || (dooct > 1)) {
+ int n = 1;
+ if ((pre == 'B') || (pre == 'b') || what == STR2NR_BIN + STR2NR_FORCE) {
+ // bin
+ if (pre != 0) {
+ n += 2; // skip over "0b"
+ }
+ while ('0' <= *ptr && *ptr <= '1') {
+ un = 2 * un + (unsigned long)(*ptr - '0');
+ ptr++;
+ if (n++ == maxlen) {
+ break;
+ }
+ }
+ } else if ((pre == '0') || what == STR2NR_OCT + STR2NR_FORCE) {
// octal
while ('0' <= *ptr && *ptr <= '7') {
un = 8 * un + (unsigned long)(*ptr - '0');
ptr++;
+ if (n++ == maxlen) {
+ break;
+ }
}
- } else if ((hex != 0) || (dohex > 1)) {
+ } else if ((pre == 'X') || (pre == 'x')
+ || what == STR2NR_HEX + STR2NR_FORCE) {
// hex
+ if (pre != 0) {
+ n += 2; // skip over "0x"
+ }
while (ascii_isxdigit(*ptr)) {
un = 16 * un + (unsigned long)hex2nr(*ptr);
ptr++;
+ if (n++ == maxlen) {
+ break;
+ }
}
} else {
// decimal
while (ascii_isdigit(*ptr)) {
un = 10 * un + (unsigned long)(*ptr - '0');
ptr++;
+ if (n++ == maxlen) {
+ break;
+ }
}
}
- if (hexp != NULL) {
- *hexp = hex;
+ if (prep != NULL) {
+ *prep = pre;
}
if (len != NULL) {
@@ -1848,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
@@ -1860,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 d311588ab4..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
@@ -1007,6 +1007,7 @@ theend:
void ex_diffsplit(exarg_T *eap)
{
win_T *old_curwin = curwin;
+ buf_T *old_curbuf = curbuf;
// don't use a new tab page, each tab page has its own diffs
cmdmod.tab = 0;
@@ -1020,8 +1021,19 @@ void ex_diffsplit(exarg_T *eap)
// split must have worked
if (curwin != old_curwin) {
// Set 'diff', 'scrollbind' on and 'wrap' off.
- diff_win_options(curwin, TRUE);
- diff_win_options(old_curwin, TRUE);
+ diff_win_options(curwin, true);
+ if (win_valid(old_curwin)) {
+ diff_win_options(old_curwin, true);
+
+ if (buf_valid(old_curbuf)) {
+ // Move the cursor position to that of the old window.
+ curwin->w_cursor.lnum = diff_get_corresponding_line(
+ old_curbuf,
+ old_curwin->w_cursor.lnum,
+ curbuf,
+ curwin->w_cursor.lnum);
+ }
+ }
}
}
}
@@ -1045,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",
@@ -1073,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;
@@ -1092,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);
}
@@ -1104,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;
}
@@ -1534,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'.
@@ -1833,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));
@@ -1863,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.
@@ -1876,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];
@@ -1887,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);
@@ -1959,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;
@@ -1982,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) {
@@ -1998,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) {
@@ -2009,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.
@@ -2375,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 8dc2844d8e..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) {
@@ -1352,24 +1346,24 @@ ins_redraw (
if (char_avail())
return;
- /* Trigger CursorMoved if the cursor moved. Not when the popup menu is
- * visible, the command might delete it. */
- if (ready && (
- has_cursormovedI()
- ||
- curwin->w_p_cole > 0
- )
+ // Trigger CursorMoved if the cursor moved. Not when the popup menu is
+ // visible, the command might delete it.
+ if (ready && (has_event(EVENT_CURSORMOVEDI) || curwin->w_p_cole > 0)
&& !equalpos(last_cursormoved, curwin->w_cursor)
- && !pum_visible()
- ) {
- /* Need to update the screen first, to make sure syntax
- * highlighting is correct after making a change (e.g., inserting
- * a "(". The autocommand may also require a redraw, so it's done
- * again below, unfortunately. */
- if (syntax_present(curwin) && must_redraw)
+ && !pum_visible()) {
+ // Need to update the screen first, to make sure syntax
+ // highlighting is correct after making a change (e.g., inserting
+ // a "(". The autocommand may also require a redraw, so it's done
+ // again below, unfortunately.
+ if (syntax_present(curwin) && must_redraw) {
update_screen(0);
- if (has_cursormovedI())
- apply_autocmds(EVENT_CURSORMOVEDI, NULL, NULL, FALSE, curbuf);
+ }
+ 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) {
conceal_old_cursor_line = last_cursormoved.lnum;
conceal_new_cursor_line = curwin->w_cursor.lnum;
@@ -1378,13 +1372,13 @@ ins_redraw (
last_cursormoved = curwin->w_cursor;
}
- /* Trigger TextChangedI if b_changedtick differs. */
- if (ready && has_textchangedI()
+ // Trigger TextChangedI if b_changedtick differs.
+ if (ready && has_event(EVENT_TEXTCHANGEDI)
&& last_changedtick != curbuf->b_changedtick
- && !pum_visible()
- ) {
- if (last_changedtick_buf == curbuf)
- apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, FALSE, curbuf);
+ && !pum_visible()) {
+ if (last_changedtick_buf == curbuf) {
+ apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, false, curbuf);
+ }
last_changedtick_buf = curbuf;
last_changedtick = curbuf->b_changedtick;
}
@@ -1798,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;
}
/*
@@ -1849,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
@@ -1927,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);
@@ -2200,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;
}
/*
@@ -2317,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).
@@ -2324,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)
@@ -2352,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();
}
@@ -2398,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;
}
@@ -2467,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 {
@@ -2551,8 +2577,12 @@ void ins_compl_show_pum(void)
}
}
- /* Compute the screen column of the start of the completed text.
- * Use the cursor to get all wrapping and other settings right. */
+ // In Replace mode when a $ is displayed at the end of the line only
+ // part of the screen would be updated. We do need to redraw here.
+ dollar_vcol = -1;
+
+ // Compute the screen column of the start of the completed text.
+ // Use the cursor to get all wrapping and other settings right.
col = curwin->w_cursor.col;
curwin->w_cursor.col = compl_col;
pum_display(compl_match_array, compl_match_arraysize, cur);
@@ -2854,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;
}
@@ -2901,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);
@@ -2926,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;
@@ -2947,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;
}
@@ -2979,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();
}
/*
@@ -3008,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;
@@ -3063,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.
@@ -3082,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
@@ -3093,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
@@ -3237,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) {
@@ -3261,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);
@@ -3294,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'.
*/
@@ -4235,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);
}
/*
@@ -4270,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:
@@ -4285,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;
}
/*
@@ -4295,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 */
@@ -4411,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;
@@ -4778,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;
@@ -4944,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;
@@ -4964,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);
@@ -5017,8 +5022,9 @@ insertchar (
int textwidth;
char_u *p;
int fo_ins_blank;
+ int force_format = flags & INSCHAR_FORMAT;
- textwidth = comp_textwidth(flags & INSCHAR_FORMAT);
+ textwidth = comp_textwidth(force_format);
fo_ins_blank = has_format_option(FO_INS_BLANK);
/*
@@ -5037,7 +5043,7 @@ insertchar (
* before 'textwidth'
*/
if (textwidth > 0
- && ((flags & INSCHAR_FORMAT)
+ && (force_format
|| (!ascii_iswhite(c)
&& !((State & REPLACE_FLAG)
&& !(State & VREPLACE_FLAG)
@@ -5051,8 +5057,11 @@ insertchar (
/* Format with 'formatexpr' when it's set. Use internal formatting
* when 'formatexpr' isn't set or it returns non-zero. */
int do_internal = TRUE;
+ colnr_T virtcol = get_nolist_virtcol()
+ + char2cells(c != NUL ? c : gchar_cursor());
- if (*curbuf->b_p_fex != NUL && (flags & INSCHAR_NO_FEX) == 0) {
+ if (*curbuf->b_p_fex != NUL && (flags & INSCHAR_NO_FEX) == 0
+ && (force_format || virtcol > (colnr_T)textwidth)) {
do_internal = (fex_format(curwin->w_cursor.lnum, 1L, c) != 0);
/* It may be required to save for undo again, e.g. when setline()
* was called. */
@@ -5120,24 +5129,20 @@ insertchar (
can_si = FALSE;
can_si_back = FALSE;
- /*
- * If there's any pending input, grab up to INPUT_BUFLEN at once.
- * This speeds up normal text input considerably.
- * Don't do this when 'cindent' or 'indentexpr' is set, because we might
- * need to re-indent at a ':', or any other character (but not what
- * 'paste' is set)..
- * Don't do this when there an InsertCharPre autocommand is defined,
- * because we need to fire the event for every character.
- */
-
- if ( !ISSPECIAL(c)
- && (!has_mbyte || (*mb_char2len)(c) == 1)
- && vpeekc() != NUL
- && !(State & REPLACE_FLAG)
- && !cindent_on()
- && !p_ri
- && !has_insertcharpre()
- ) {
+ // If there's any pending input, grab up to INPUT_BUFLEN at once.
+ // This speeds up normal text input considerably.
+ // Don't do this when 'cindent' or 'indentexpr' is set, because we might
+ // need to re-indent at a ':', or any other character (but not what
+ // 'paste' is set)..
+ // Don't do this when there an InsertCharPre autocommand is defined,
+ // because we need to fire the event for every character.
+ if (!ISSPECIAL(c)
+ && (!has_mbyte || (*mb_char2len)(c) == 1)
+ && vpeekc() != NUL
+ && !(State & REPLACE_FLAG)
+ && !cindent_on()
+ && !p_ri
+ && !has_event(EVENT_INSERTCHARPRE)) {
#define INPUT_BUFLEN 100
char_u buf[INPUT_BUFLEN + 1];
int i;
@@ -6316,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);
@@ -6563,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);
}
/*
@@ -6580,9 +6587,14 @@ static int cindent_on(void) {
*/
void fixthisline(IndentGetter get_the_indent)
{
- change_indent(INDENT_SET, get_the_indent(), FALSE, 0, TRUE);
- if (linewhite(curwin->w_cursor.lnum))
- did_ai = TRUE; /* delete the indent if the line stays empty */
+ int amount = get_the_indent();
+
+ if (amount >= 0) {
+ change_indent(INDENT_SET, amount, false, 0, true);
+ if (linewhite(curwin->w_cursor.lnum)) {
+ did_ai = true; // delete the indent if the line stays empty
+ }
+ }
}
void fix_indent(void) {
@@ -6594,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' */
@@ -6635,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] == ':'
@@ -6706,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++;
@@ -6798,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++;
}
/*
@@ -6817,7 +6825,7 @@ int in_cinkeys(int keytyped, int when, int line_is_empty)
*/
look = skip_to_option_part(look);
}
- return FALSE;
+ return false;
}
/*
@@ -7044,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) {
/*
@@ -7094,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();
@@ -7146,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;
}
/*
@@ -7193,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:
@@ -7211,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;
}
/*
@@ -7250,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 */
@@ -7360,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 */
@@ -7382,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;
}
@@ -7427,15 +7434,14 @@ static int ins_bs(int c, int mode, int *inserted_space_p)
* delete newline!
*/
if (curwin->w_cursor.col == 0) {
- lnum = Insstart_orig.lnum;
+ 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_orig.lnum;
- Insstart_orig.col = MAXCOL;
- Insstart = Insstart_orig;
+ Insstart.lnum--;
+ Insstart.col = MAXCOL;
}
/*
* In replace mode:
@@ -7585,27 +7591,35 @@ static int ins_bs(int c, int mode, int *inserted_space_p)
* happen when using 'sts' and 'linebreak'. */
if (vcol >= start_vcol)
ins_bs_one(&vcol);
- }
- /*
- * Delete upto starting point, start of line or previous word.
- */
- else do {
- if (!revins_on) /* put cursor on char to be deleted */
- dec_cursor();
- /* start of word? */
- if (mode == BACKSPACE_WORD && !ascii_isspace(gchar_cursor())) {
- mode = BACKSPACE_WORD_NOT_SPACE;
- temp = vim_iswordc(gchar_cursor());
+ // Delete upto starting point, start of line or previous word.
+ } else {
+ int cclass = 0, prev_cclass = 0;
+
+ if (has_mbyte) {
+ cclass = mb_get_class(get_cursor_pos_ptr());
+ }
+ do {
+ if (!revins_on) { // put cursor on char to be deleted
+ dec_cursor();
+ }
+ cc = gchar_cursor();
+ // look multi-byte character class
+ if (has_mbyte) {
+ prev_cclass = cclass;
+ cclass = mb_get_class(get_cursor_pos_ptr());
}
- /* end of word? */
- else if (mode == BACKSPACE_WORD_NOT_SPACE
- && (ascii_isspace(cc = gchar_cursor())
- || vim_iswordc(cc) != temp)) {
- if (!revins_on)
+ if (mode == BACKSPACE_WORD && !ascii_isspace(cc)) { // start of word?
+ mode = BACKSPACE_WORD_NOT_SPACE;
+ temp = vim_iswordc(cc);
+ } else if (mode == BACKSPACE_WORD_NOT_SPACE
+ && ((ascii_isspace(cc) || vim_iswordc(cc) != temp)
+ || prev_cclass != cclass)) { // end of word?
+ if (!revins_on) {
inc_cursor();
- else if (State & REPLACE_FLAG)
+ } else if (State & REPLACE_FLAG) {
dec_cursor();
+ }
break;
}
if (State & REPLACE_FLAG)
@@ -7630,26 +7644,26 @@ 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)));
- did_backspace = TRUE;
- }
- did_si = FALSE;
- can_si = FALSE;
- can_si_back = FALSE;
- if (curwin->w_cursor.col <= 1)
- did_ai = FALSE;
- /*
- * It's a little strange to put backspaces into the redo
- * buffer, but it makes auto-indent a lot easier to deal
- * with.
- */
+ }
+ } 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;
+ }
+ did_si = false;
+ can_si = false;
+ can_si_back = false;
+ if (curwin->w_cursor.col <= 1) {
+ did_ai = false;
+ }
+ // It's a little strange to put backspaces into the redo
+ // buffer, but it makes auto-indent a lot easier to deal
+ // with.
AppendCharToRedobuff(c);
/* If deleted before the insertion point, adjust it */
@@ -7668,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;
}
@@ -7739,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;
}
@@ -7992,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;
@@ -8028,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;
/*
@@ -8173,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();
/*
@@ -8220,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. */
@@ -8476,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_insertcharpre())
+ // Return quickly when there is nothing to do.
+ if (!has_event(EVENT_INSERTCHARPRE)) {
return NULL;
-
- 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;
}
- /* 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)) {
@@ -8502,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 4a2bf2ac7a..712ee06b85 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -66,13 +66,14 @@
#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"
@@ -87,7 +88,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 +142,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 +153,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 +167,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 +178,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,92 +281,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("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
@@ -415,29 +420,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;
@@ -456,17 +438,6 @@ 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 const char *const msgpack_type_names[] = {
[kMPNil] = "nil",
[kMPBoolean] = "boolean",
@@ -478,7 +449,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,
@@ -495,8 +466,9 @@ 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;
struct vimvar *p;
init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
@@ -505,7 +477,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)
@@ -534,7 +506,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);
@@ -544,9 +516,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)
@@ -554,7 +536,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);
@@ -789,45 +771,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;
@@ -840,7 +827,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;
@@ -1111,19 +1099,17 @@ typval_T *eval_expr(char_u *arg, char_u **nextcmd)
}
-/*
- * Call some vimL function and return the result in "*rettv".
- * Uses argv[argc] for the function arguments. Only Number and String
- * arguments are currently supported.
- * Returns OK or FAIL.
- */
-int
-call_vim_function (
+// Call some vimL function and return the result in "*rettv".
+// Uses argv[argc] for the function arguments. Only Number and String
+// arguments are currently supported.
+//
+// Return OK or FAIL.
+int call_vim_function(
char_u *func,
int argc,
char_u **argv,
- int safe, /* use the sandbox */
- int str_arg_only, /* all arguments are strings */
+ int safe, // use the sandbox
+ int str_arg_only, // all arguments are strings
typval_T *rettv
)
{
@@ -1136,18 +1122,19 @@ call_vim_function (
typval_T *argvars = xmalloc((argc + 1) * sizeof(typval_T));
for (int i = 0; i < argc; i++) {
- /* Pass a NULL or empty argument as an empty string */
+ // Pass a NULL or empty argument as an empty string
if (argv[i] == NULL || *argv[i] == NUL) {
argvars[i].v_type = VAR_STRING;
argvars[i].vval.v_string = (char_u *)"";
continue;
}
- if (str_arg_only)
+ if (str_arg_only) {
len = 0;
- else
- /* Recognize a number argument, the others must be strings. */
- vim_str2nr(argv[i], NULL, &len, TRUE, TRUE, &n, NULL);
+ } else {
+ // Recognize a number argument, the others must be strings.
+ vim_str2nr(argv[i], NULL, &len, STR2NR_ALL, &n, NULL, 0);
+ }
if (len != 0 && len == (int)STRLEN(argv[i])) {
argvars[i].v_type = VAR_NUMBER;
argvars[i].vval.v_number = n;
@@ -1164,16 +1151,17 @@ call_vim_function (
rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */
ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars,
- curwin->w_cursor.lnum, curwin->w_cursor.lnum,
- &doesrange, TRUE, NULL);
+ curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+ &doesrange, true, NULL);
if (safe) {
--sandbox;
restore_funccal(save_funccalp);
}
xfree(argvars);
- if (ret == FAIL)
+ if (ret == FAIL) {
clear_tv(rettv);
+ }
return ret;
}
@@ -1698,12 +1686,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;
@@ -1723,7 +1712,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 *)"",
@@ -2174,10 +2163,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);
@@ -2272,11 +2261,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
@@ -2284,16 +2278,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;
@@ -2401,11 +2396,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:
@@ -2473,6 +2469,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);
}
}
@@ -2888,16 +2887,19 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit)
else if (do_unlet(lp->ll_name, forceit) == FAIL)
ret = FAIL;
*name_end = cc;
- } else if (tv_check_lock(lp->ll_tv->v_lock, lp->ll_name))
+ } else if ((lp->ll_list != NULL
+ && 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, false))) {
return FAIL;
- else if (lp->ll_range) {
+ } else if (lp->ll_range) {
listitem_T *li;
listitem_T *ll_li = lp->ll_li;
int ll_n1 = lp->ll_n1;
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;
@@ -2951,17 +2953,30 @@ int do_unlet(char_u *name, int forceit)
hashtab_T *ht;
hashitem_T *hi;
char_u *varname;
+ dict_T *d;
dictitem_T *di;
dict_T *dict;
ht = find_var_ht_dict(name, &varname, &dict);
if (ht != NULL && *varname != NUL) {
+ if (ht == &globvarht) {
+ d = &globvardict;
+ } else if (current_funccal != NULL
+ && ht == &current_funccal->l_vars.dv_hashtab) {
+ d = &current_funccal->l_vars;
+ } else {
+ di = find_var_in_ht(ht, *name, (char_u *)"", false);
+ d = di->di_tv.vval.v_dict;
+ }
+
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))
+ 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;
bool watched = is_watched(dict);
@@ -3027,12 +3042,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;
}
@@ -3093,6 +3109,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;
}
@@ -3171,7 +3196,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;
@@ -3233,9 +3258,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;
@@ -3554,9 +3580,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;
}
@@ -3983,12 +4010,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;
@@ -4023,38 +4060,35 @@ eval6 (
return OK;
}
-/*
- * Handle sixth level expression:
- * number number constant
- * "string" string constant
- * 'string' literal string constant
- * &option-name option value
- * @r register contents
- * identifier variable value
- * function() function call
- * $VAR environment variable
- * (expression) nested expression
- * [expr, expr] List
- * {key: val, key: val} Dictionary
- *
- * Also handle:
- * ! in front logical NOT
- * - in front unary minus
- * + in front unary plus (ignored)
- * trailing [] subscript in String or List
- * trailing .name entry in Dictionary
- *
- * "arg" must point to the first non-white of the expression.
- * "arg" is advanced to the next non-white after the recognized expression.
- *
- * Return OK or FAIL.
- */
-static int
-eval7 (
+// Handle sixth level expression:
+// number number constant
+// "string" string constant
+// 'string' literal string constant
+// &option-name option value
+// @r register contents
+// identifier variable value
+// function() function call
+// $VAR environment variable
+// (expression) nested expression
+// [expr, expr] List
+// {key: val, key: val} Dictionary
+//
+// Also handle:
+// ! in front logical NOT
+// - in front unary minus
+// + in front unary plus (ignored)
+// trailing [] subscript in String or List
+// trailing .name entry in Dictionary
+//
+// "arg" must point to the first non-white of the expression.
+// "arg" is advanced to the next non-white after the recognized expression.
+//
+// Return OK or FAIL.
+static int eval7(
char_u **arg,
typval_T *rettv,
int evaluate,
- int want_string /* after "." operator */
+ int want_string // after "." operator
)
{
long n;
@@ -4064,24 +4098,19 @@ eval7 (
int ret = OK;
char_u *alias;
- /*
- * Initialise variable so that clear_tv() can't mistake this for a
- * string and free a string that isn't there.
- */
+ // Initialise variable so that clear_tv() can't mistake this for a
+ // string and free a string that isn't there.
rettv->v_type = VAR_UNKNOWN;
- /*
- * Skip '!' and '-' characters. They are handled later.
- */
+ // Skip '!' and '-' characters. They are handled later.
start_leader = *arg;
- while (**arg == '!' || **arg == '-' || **arg == '+')
+ while (**arg == '!' || **arg == '-' || **arg == '+') {
*arg = skipwhite(*arg + 1);
+ }
end_leader = *arg;
switch (**arg) {
- /*
- * Number constant.
- */
+ // Number constant.
case '0':
case '1':
case '2':
@@ -4094,38 +4123,41 @@ eval7 (
case '9':
{
char_u *p = skipdigits(*arg + 1);
- int get_float = FALSE;
+ int get_float = false;
- /* We accept a float when the format matches
- * "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very
- * strict to avoid backwards compatibility problems.
- * Don't look for a float after the "." operator, so that
- * ":let vers = 1.2.3" doesn't fail. */
+ // We accept a float when the format matches
+ // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very
+ // strict to avoid backwards compatibility problems.
+ // Don't look for a float after the "." operator, so that
+ // ":let vers = 1.2.3" doesn't fail.
if (!want_string && p[0] == '.' && ascii_isdigit(p[1])) {
- get_float = TRUE;
+ get_float = true;
p = skipdigits(p + 2);
if (*p == 'e' || *p == 'E') {
++p;
- if (*p == '-' || *p == '+')
+ if (*p == '-' || *p == '+') {
++p;
- if (!ascii_isdigit(*p))
- get_float = FALSE;
- else
+ }
+ if (!ascii_isdigit(*p)) {
+ get_float = false;
+ } else {
p = skipdigits(p + 1);
+ }
+ }
+ if (ASCII_ISALPHA(*p) || *p == '.') {
+ get_float = false;
}
- if (ASCII_ISALPHA(*p) || *p == '.')
- get_float = FALSE;
}
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;
}
} else {
- vim_str2nr(*arg, NULL, &len, TRUE, TRUE, &n, NULL);
+ vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0);
*arg += len;
if (evaluate) {
rettv->v_type = VAR_NUMBER;
@@ -4135,62 +4167,47 @@ eval7 (
break;
}
- /*
- * String constant: "string".
- */
+ // String constant: "string".
case '"': ret = get_string_tv(arg, rettv, evaluate);
break;
- /*
- * Literal string constant: 'str''ing'.
- */
+ // Literal string constant: 'str''ing'.
case '\'': ret = get_lit_string_tv(arg, rettv, evaluate);
break;
- /*
- * List: [expr, expr]
- */
+ // List: [expr, expr]
case '[': ret = get_list_tv(arg, rettv, evaluate);
break;
- /*
- * Dictionary: {key: val, key: val}
- */
+ // Dictionary: {key: val, key: val}
case '{': ret = get_dict_tv(arg, rettv, evaluate);
break;
- /*
- * Option value: &name
- */
+ // Option value: &name
case '&': ret = get_option_tv(arg, rettv, evaluate);
break;
- /*
- * Environment variable: $VAR.
- */
+ // Environment variable: $VAR.
case '$': ret = get_env_tv(arg, rettv, evaluate);
break;
- /*
- * Register contents: @r.
- */
+ // Register contents: @r.
case '@': ++*arg;
if (evaluate) {
rettv->v_type = VAR_STRING;
rettv->vval.v_string = get_reg_contents(**arg, kGRegExprSrc);
}
- if (**arg != NUL)
+ if (**arg != NUL) {
++*arg;
+ }
break;
- /*
- * nested expression: (expression).
- */
+ // nested expression: (expression).
case '(': *arg = skipwhite(*arg + 1);
- ret = eval1(arg, rettv, evaluate); /* recursive! */
- if (**arg == ')')
+ ret = eval1(arg, rettv, evaluate); // recursive!
+ if (**arg == ')') {
++*arg;
- else if (ret == OK) {
+ } else if (ret == OK) {
EMSG(_("E110: Missing ')'"));
clear_tv(rettv);
ret = FAIL;
@@ -4202,71 +4219,72 @@ eval7 (
}
if (ret == NOTDONE) {
- /*
- * Must be a variable or function name.
- * Can also be a curly-braces kind of name: {expr}.
- */
+ // Must be a variable or function name.
+ // Can also be a curly-braces kind of name: {expr}.
s = *arg;
- len = get_name_len(arg, &alias, evaluate, TRUE);
- if (alias != NULL)
+ len = get_name_len(arg, &alias, evaluate, true);
+ if (alias != NULL) {
s = alias;
+ }
- if (len <= 0)
+ if (len <= 0) {
ret = FAIL;
- else {
- if (**arg == '(') { /* recursive! */
- /* If "s" is the name of a variable of type VAR_FUNC
- * use its contents. */
+ } else {
+ if (**arg == '(') { // recursive!
+ // If "s" is the name of a variable of type VAR_FUNC
+ // use its contents.
s = deref_func_name(s, &len, !evaluate);
- /* Invoke the function. */
+ // Invoke the function.
ret = get_func_tv(s, len, rettv, arg,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&len, evaluate, NULL);
- /* If evaluate is FALSE rettv->v_type was not set in
- * get_func_tv, but it's needed in handle_subscript() to parse
- * what follows. So set it here. */
+ // If evaluate is false rettv->v_type was not set in
+ // get_func_tv, but it's needed in handle_subscript() to parse
+ // what follows. So set it here.
if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') {
rettv->vval.v_string = empty_string;
rettv->v_type = VAR_FUNC;
}
- /* Stop the expression evaluation when immediately
- * aborting on error, or when an interrupt occurred or
- * an exception was thrown but not caught. */
+ // Stop the expression evaluation when immediately
+ // aborting on error, or when an interrupt occurred or
+ // an exception was thrown but not caught.
if (aborting()) {
- if (ret == OK)
+ if (ret == OK) {
clear_tv(rettv);
+ }
ret = FAIL;
}
- } else if (evaluate)
- ret = get_var_tv(s, len, rettv, TRUE, FALSE);
- else
+ } else if (evaluate) {
+ ret = get_var_tv(s, len, rettv, NULL, true, false);
+ } else {
ret = OK;
+ }
}
xfree(alias);
}
*arg = skipwhite(*arg);
- /* Handle following '[', '(' and '.' for expr[expr], expr.name,
- * expr(expr). */
- if (ret == OK)
- ret = handle_subscript(arg, rettv, evaluate, TRUE);
+ // Handle following '[', '(' and '.' for expr[expr], expr.name,
+ // expr(expr).
+ if (ret == OK) {
+ ret = handle_subscript(arg, rettv, evaluate, true);
+ }
- /*
- * Apply logical NOT and unary '-', from right to left, ignore '+'.
- */
+ // Apply logical NOT and unary '-', from right to left, ignore '+'.
if (ret == OK && evaluate && end_leader > start_leader) {
- int error = FALSE;
+ int error = false;
int val = 0;
float_T f = 0.0;
- if (rettv->v_type == VAR_FLOAT)
+ if (rettv->v_type == VAR_FLOAT) {
f = rettv->vval.v_float;
- else
+ } else {
val = get_tv_number_chk(rettv, &error);
+ }
if (error) {
clear_tv(rettv);
ret = FAIL;
@@ -4274,15 +4292,17 @@ eval7 (
while (end_leader > start_leader) {
--end_leader;
if (*end_leader == '!') {
- if (rettv->v_type == VAR_FLOAT)
+ if (rettv->v_type == VAR_FLOAT) {
f = !f;
- else
+ } else {
val = !val;
+ }
} else if (*end_leader == '-') {
- if (rettv->v_type == VAR_FLOAT)
+ if (rettv->v_type == VAR_FLOAT) {
f = -f;
- else
+ } else {
val = -val;
+ }
}
}
if (rettv->v_type == VAR_FLOAT) {
@@ -4320,14 +4340,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);
@@ -4518,6 +4561,11 @@ eval_index (
*rettv = var1;
}
break;
+ case VAR_SPECIAL:
+ case VAR_FUNC:
+ case VAR_FLOAT:
+ case VAR_UNKNOWN:
+ break; // Not evaluating, skipping over subscript
}
}
@@ -4653,10 +4701,13 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
int n, nr;
int c = toupper(*p);
- if (c == 'X')
+ if (c == 'X') {
n = 2;
- else
+ } else if (*p == 'u') {
n = 4;
+ } else {
+ n = 8;
+ }
nr = 0;
while (--n >= 0 && ascii_isxdigit(p[1])) {
++p;
@@ -4689,13 +4740,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;
@@ -5059,10 +5111,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;
}
/*
@@ -5463,9 +5523,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;
@@ -5473,9 +5534,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
@@ -5527,6 +5585,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.
*
@@ -5562,8 +5636,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.
@@ -5651,6 +5724,14 @@ bool garbage_collect(void)
ABORTING(set_ref_in_ht)(&fc->l_avars.dv_hashtab, copyID, NULL);
}
+ // Jobs
+ {
+ TerminalJobData *data;
+ map_foreach_value(jobs, data, {
+ ABORTING(set_ref_dict)(data->self, copyID);
+ })
+ }
+
// v: vars
ABORTING(set_ref_in_ht)(&vimvarht, copyID, NULL);
@@ -5728,8 +5809,7 @@ static int free_unref_items(int copyID)
// Go through the list of dicts and free items without the copyID.
// Don't free dicts that are referenced internally.
for (dict_T *dd = first_dict; dd != NULL; ) {
- if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
- && !dd->internal_refcount) {
+ if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) {
// Free the Dictionary and ordinary items it contains, but don't
// recurse into Lists and Dictionaries, they will be in the list
// of dicts or list of lists. */
@@ -5901,6 +5981,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;
}
@@ -5970,7 +6059,6 @@ dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET
d->dv_scope = 0;
d->dv_refcount = 0;
d->dv_copyID = 0;
- d->internal_refcount = 0;
QUEUE_INIT(&d->watchers);
return d;
@@ -5988,6 +6076,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
@@ -6061,7 +6170,7 @@ dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET
#ifndef __clang_analyzer__
STRCPY(di->di_key, key);
#endif
- di->di_flags = 0;
+ di->di_flags = DI_FLAGS_ALLOC;
return di;
}
@@ -6073,7 +6182,7 @@ static dictitem_T *dictitem_copy(dictitem_T *org) FUNC_ATTR_NONNULL_RET
dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(org->di_key));
STRCPY(di->di_key, org->di_key);
- di->di_flags = 0;
+ di->di_flags = DI_FLAGS_ALLOC;
copy_tv(&org->di_tv, &di->di_tv);
return di;
@@ -6101,7 +6210,9 @@ static void dictitem_remove(dict_T *dict, dictitem_T *item)
void dictitem_free(dictitem_T *item)
{
clear_tv(&item->di_tv);
- xfree(item);
+ if (item->di_flags & DI_FLAGS_ALLOC) {
+ xfree(item);
+ }
}
/// Make a copy of dictionary
@@ -6227,6 +6338,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.
*/
@@ -6452,577 +6581,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) \
- 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) \
- 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) == 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) \
- == 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) \
- == 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().
-///
-/// @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 *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);
- 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.
+/// 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 *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);
- 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.
@@ -7081,295 +6655,304 @@ static struct fst {
/* implementation of function */
} functions[] =
{
- {"abs", 1, 1, f_abs},
- {"acos", 1, 1, f_acos}, /* WJMc */
- {"add", 2, 2, f_add},
- {"and", 2, 2, f_and},
- {"append", 2, 2, f_append},
- {"argc", 0, 0, f_argc},
- {"argidx", 0, 0, f_argidx},
- {"arglistid", 0, 2, f_arglistid},
- {"argv", 0, 1, f_argv},
- {"asin", 1, 1, f_asin}, /* WJMc */
- {"atan", 1, 1, f_atan},
- {"atan2", 2, 2, f_atan2},
- {"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 */
- {"buflisted", 1, 1, f_buflisted},
- {"bufloaded", 1, 1, f_bufloaded},
- {"bufname", 1, 1, f_bufname},
- {"bufnr", 1, 2, f_bufnr},
- {"bufwinnr", 1, 1, f_bufwinnr},
- {"byte2line", 1, 1, f_byte2line},
- {"byteidx", 2, 2, f_byteidx},
- {"byteidxcomp", 2, 2, f_byteidxcomp},
- {"call", 2, 3, f_call},
- {"ceil", 1, 1, f_ceil},
- {"changenr", 0, 0, f_changenr},
- {"char2nr", 1, 2, f_char2nr},
- {"cindent", 1, 1, f_cindent},
- {"clearmatches", 0, 0, f_clearmatches},
- {"col", 1, 1, f_col},
- {"complete", 2, 2, f_complete},
- {"complete_add", 1, 1, f_complete_add},
- {"complete_check", 0, 0, f_complete_check},
- {"confirm", 1, 4, f_confirm},
- {"copy", 1, 1, f_copy},
- {"cos", 1, 1, f_cos},
- {"cosh", 1, 1, f_cosh},
- {"count", 2, 4, f_count},
- {"cscope_connection",0,3, f_cscope_connection},
- {"cursor", 1, 3, f_cursor},
- {"deepcopy", 1, 2, f_deepcopy},
- {"delete", 1, 1, f_delete},
- {"dictwatcheradd", 3, 3, f_dictwatcheradd},
- {"dictwatcherdel", 3, 3, f_dictwatcherdel},
- {"did_filetype", 0, 0, f_did_filetype},
- {"diff_filler", 1, 1, f_diff_filler},
- {"diff_hlID", 2, 2, f_diff_hlID},
- {"empty", 1, 1, f_empty},
- {"escape", 2, 2, f_escape},
- {"eval", 1, 1, f_eval},
- {"eventhandler", 0, 0, f_eventhandler},
- {"executable", 1, 1, f_executable},
- {"exepath", 1, 1, f_exepath},
- {"exists", 1, 1, f_exists},
- {"exp", 1, 1, f_exp},
- {"expand", 1, 3, f_expand},
- {"extend", 2, 3, f_extend},
- {"feedkeys", 1, 2, f_feedkeys},
- {"file_readable", 1, 1, f_filereadable}, /* obsolete */
- {"filereadable", 1, 1, f_filereadable},
- {"filewritable", 1, 1, f_filewritable},
- {"filter", 2, 2, f_filter},
- {"finddir", 1, 3, f_finddir},
- {"findfile", 1, 3, f_findfile},
- {"float2nr", 1, 1, f_float2nr},
- {"floor", 1, 1, f_floor},
- {"fmod", 2, 2, f_fmod},
- {"fnameescape", 1, 1, f_fnameescape},
- {"fnamemodify", 2, 2, f_fnamemodify},
- {"foldclosed", 1, 1, f_foldclosed},
- {"foldclosedend", 1, 1, f_foldclosedend},
- {"foldlevel", 1, 1, f_foldlevel},
- {"foldtext", 0, 0, f_foldtext},
- {"foldtextresult", 1, 1, f_foldtextresult},
- {"foreground", 0, 0, f_foreground},
- {"function", 1, 1, f_function},
- {"garbagecollect", 0, 1, f_garbagecollect},
- {"get", 2, 3, f_get},
- {"getbufline", 2, 3, f_getbufline},
- {"getbufvar", 2, 3, f_getbufvar},
- {"getchar", 0, 1, f_getchar},
- {"getcharmod", 0, 0, f_getcharmod},
- {"getcharsearch", 0, 0, f_getcharsearch},
- {"getcmdline", 0, 0, f_getcmdline},
- {"getcmdpos", 0, 0, f_getcmdpos},
- {"getcmdtype", 0, 0, f_getcmdtype},
- {"getcmdwintype", 0, 0, f_getcmdwintype},
- {"getcurpos", 0, 0, f_getcurpos},
- {"getcwd", 0, 0, f_getcwd},
- {"getfontname", 0, 1, f_getfontname},
- {"getfperm", 1, 1, f_getfperm},
- {"getfsize", 1, 1, f_getfsize},
- {"getftime", 1, 1, f_getftime},
- {"getftype", 1, 1, f_getftype},
- {"getline", 1, 2, f_getline},
- {"getloclist", 1, 1, f_getqflist},
- {"getmatches", 0, 0, f_getmatches},
- {"getpid", 0, 0, f_getpid},
- {"getpos", 1, 1, f_getpos},
- {"getqflist", 0, 0, f_getqflist},
- {"getreg", 0, 3, f_getreg},
- {"getregtype", 0, 1, f_getregtype},
- {"gettabvar", 2, 3, f_gettabvar},
- {"gettabwinvar", 3, 4, f_gettabwinvar},
- {"getwinposx", 0, 0, f_getwinposx},
- {"getwinposy", 0, 0, f_getwinposy},
- {"getwinvar", 2, 3, f_getwinvar},
- {"glob", 1, 3, f_glob},
- {"glob2regpat", 1, 1, f_glob2regpat},
- {"globpath", 2, 4, f_globpath},
- {"has", 1, 1, f_has},
- {"has_key", 2, 2, f_has_key},
- {"haslocaldir", 0, 0, f_haslocaldir},
- {"hasmapto", 1, 3, f_hasmapto},
- {"highlightID", 1, 1, f_hlID}, /* obsolete */
- {"highlight_exists",1, 1, f_hlexists}, /* obsolete */
- {"histadd", 2, 2, f_histadd},
- {"histdel", 1, 2, f_histdel},
- {"histget", 1, 2, f_histget},
- {"histnr", 1, 1, f_histnr},
- {"hlID", 1, 1, f_hlID},
- {"hlexists", 1, 1, f_hlexists},
- {"hostname", 0, 0, f_hostname},
- {"iconv", 3, 3, f_iconv},
- {"indent", 1, 1, f_indent},
- {"index", 2, 4, f_index},
- {"input", 1, 3, f_input},
- {"inputdialog", 1, 3, f_inputdialog},
- {"inputlist", 1, 1, f_inputlist},
- {"inputrestore", 0, 0, f_inputrestore},
- {"inputsave", 0, 0, f_inputsave},
- {"inputsecret", 1, 2, f_inputsecret},
- {"insert", 2, 3, f_insert},
- {"invert", 1, 1, f_invert},
- {"isdirectory", 1, 1, f_isdirectory},
- {"islocked", 1, 1, f_islocked},
- {"items", 1, 1, f_items},
- {"jobclose", 1, 2, f_jobclose},
- {"jobresize", 3, 3, f_jobresize},
- {"jobsend", 2, 2, f_jobsend},
- {"jobstart", 1, 2, f_jobstart},
- {"jobstop", 1, 1, f_jobstop},
- {"jobwait", 1, 2, f_jobwait},
- {"join", 1, 2, f_join},
- {"keys", 1, 1, f_keys},
- {"last_buffer_nr", 0, 0, f_last_buffer_nr}, /* obsolete */
- {"len", 1, 1, f_len},
- {"libcall", 3, 3, f_libcall},
- {"libcallnr", 3, 3, f_libcallnr},
- {"line", 1, 1, f_line},
- {"line2byte", 1, 1, f_line2byte},
- {"lispindent", 1, 1, f_lispindent},
- {"localtime", 0, 0, f_localtime},
- {"log", 1, 1, f_log},
- {"log10", 1, 1, f_log10},
- {"map", 2, 2, f_map},
- {"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},
- {"matcharg", 1, 1, f_matcharg},
- {"matchdelete", 1, 1, f_matchdelete},
- {"matchend", 2, 4, f_matchend},
- {"matchlist", 2, 4, f_matchlist},
- {"matchstr", 2, 4, f_matchstr},
- {"max", 1, 1, f_max},
- {"min", 1, 1, f_min},
- {"mkdir", 1, 3, f_mkdir},
- {"mode", 0, 1, f_mode},
- {"msgpackdump", 1, 1, f_msgpackdump},
- {"msgpackparse", 1, 1, f_msgpackparse},
- {"nextnonblank", 1, 1, f_nextnonblank},
- {"nr2char", 1, 2, f_nr2char},
- {"or", 2, 2, f_or},
- {"pathshorten", 1, 1, f_pathshorten},
- {"pow", 2, 2, f_pow},
- {"prevnonblank", 1, 1, f_prevnonblank},
- {"printf", 2, 19, 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},
- {"reltimestr", 1, 1, f_reltimestr},
- {"remove", 2, 3, f_remove},
- {"rename", 2, 2, f_rename},
- {"repeat", 2, 2, f_repeat},
- {"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},
- {"rpcstart", 1, 2, f_rpcstart},
- {"rpcstop", 1, 1, f_rpcstop},
- {"screenattr", 2, 2, f_screenattr},
- {"screenchar", 2, 2, f_screenchar},
- {"screencol", 0, 0, f_screencol},
- {"screenrow", 0, 0, f_screenrow},
- {"search", 1, 4, f_search},
- {"searchdecl", 1, 3, f_searchdecl},
- {"searchpair", 3, 7, f_searchpair},
- {"searchpairpos", 3, 7, f_searchpairpos},
- {"searchpos", 1, 4, f_searchpos},
- {"serverlist", 0, 0, f_serverlist},
- {"serverstart", 0, 1, f_serverstart},
- {"serverstop", 1, 1, f_serverstop},
- {"setbufvar", 3, 3, f_setbufvar},
- {"setcharsearch", 1, 1, f_setcharsearch},
- {"setcmdpos", 1, 1, f_setcmdpos},
- {"setline", 2, 2, f_setline},
- {"setloclist", 2, 3, f_setloclist},
- {"setmatches", 1, 1, f_setmatches},
- {"setpos", 2, 2, f_setpos},
- {"setqflist", 1, 2, f_setqflist},
- {"setreg", 2, 3, f_setreg},
- {"settabvar", 3, 3, f_settabvar},
- {"settabwinvar", 4, 4, f_settabwinvar},
- {"setwinvar", 3, 3, f_setwinvar},
- {"sha256", 1, 1, f_sha256},
- {"shellescape", 1, 2, f_shellescape},
- {"shiftwidth", 0, 0, f_shiftwidth},
- {"simplify", 1, 1, f_simplify},
- {"sin", 1, 1, f_sin},
- {"sinh", 1, 1, f_sinh},
- {"sort", 1, 3, f_sort},
- {"soundfold", 1, 1, f_soundfold},
- {"spellbadword", 0, 1, f_spellbadword},
- {"spellsuggest", 1, 3, f_spellsuggest},
- {"split", 1, 3, f_split},
- {"sqrt", 1, 1, f_sqrt},
- {"str2float", 1, 1, f_str2float},
- {"str2nr", 1, 2, f_str2nr},
- {"strchars", 1, 1, f_strchars},
- {"strdisplaywidth", 1, 2, f_strdisplaywidth},
- {"strftime", 1, 2, f_strftime},
- {"stridx", 2, 3, f_stridx},
- {"string", 1, 1, f_string},
- {"strlen", 1, 1, f_strlen},
- {"strpart", 2, 3, f_strpart},
- {"strridx", 2, 3, f_strridx},
- {"strtrans", 1, 1, f_strtrans},
- {"strwidth", 1, 1, f_strwidth},
- {"submatch", 1, 2, f_submatch},
- {"substitute", 4, 4, f_substitute},
- {"synID", 3, 3, f_synID},
- {"synIDattr", 2, 3, f_synIDattr},
- {"synIDtrans", 1, 1, f_synIDtrans},
- {"synconcealed", 2, 2, f_synconcealed},
- {"synstack", 2, 2, f_synstack},
- {"system", 1, 2, f_system},
- {"systemlist", 1, 3, f_systemlist},
- {"tabpagebuflist", 0, 1, f_tabpagebuflist},
- {"tabpagenr", 0, 1, f_tabpagenr},
- {"tabpagewinnr", 1, 2, f_tabpagewinnr},
- {"tagfiles", 0, 0, f_tagfiles},
- {"taglist", 1, 1, f_taglist},
- {"tan", 1, 1, f_tan},
- {"tanh", 1, 1, f_tanh},
- {"tempname", 0, 0, f_tempname},
- {"termopen", 1, 2, f_termopen},
- {"test", 1, 1, f_test},
- {"tolower", 1, 1, f_tolower},
- {"toupper", 1, 1, f_toupper},
- {"tr", 3, 3, f_tr},
- {"trunc", 1, 1, f_trunc},
- {"type", 1, 1, f_type},
- {"undofile", 1, 1, f_undofile},
- {"undotree", 0, 0, f_undotree},
- {"uniq", 1, 3, f_uniq},
- {"values", 1, 1, f_values},
- {"virtcol", 1, 1, f_virtcol},
- {"visualmode", 0, 1, f_visualmode},
- {"wildmenumode", 0, 0, f_wildmenumode},
- {"winbufnr", 1, 1, f_winbufnr},
- {"wincol", 0, 0, f_wincol},
- {"winheight", 1, 1, f_winheight},
- {"winline", 0, 0, f_winline},
- {"winnr", 0, 1, f_winnr},
- {"winrestcmd", 0, 0, f_winrestcmd},
- {"winrestview", 1, 1, f_winrestview},
- {"winsaveview", 0, 0, f_winsaveview},
- {"winwidth", 1, 1, f_winwidth},
- {"writefile", 2, 3, f_writefile},
- {"xor", 2, 2, f_xor},
+ { "abs", 1, 1, f_abs },
+ { "acos", 1, 1, f_acos }, // WJMc
+ { "add", 2, 2, f_add },
+ { "and", 2, 2, f_and },
+ { "append", 2, 2, f_append },
+ { "argc", 0, 0, f_argc },
+ { "argidx", 0, 0, f_argidx },
+ { "arglistid", 0, 2, f_arglistid },
+ { "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_false", 1, 2, f_assert_false },
+ { "assert_true", 1, 2, f_assert_true },
+ { "atan", 1, 1, f_atan },
+ { "atan2", 2, 2, f_atan2 },
+ { "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
+ { "buflisted", 1, 1, f_buflisted },
+ { "bufloaded", 1, 1, f_bufloaded },
+ { "bufname", 1, 1, f_bufname },
+ { "bufnr", 1, 2, f_bufnr },
+ { "bufwinnr", 1, 1, f_bufwinnr },
+ { "byte2line", 1, 1, f_byte2line },
+ { "byteidx", 2, 2, f_byteidx },
+ { "byteidxcomp", 2, 2, f_byteidxcomp },
+ { "call", 2, 3, f_call },
+ { "ceil", 1, 1, f_ceil },
+ { "changenr", 0, 0, f_changenr },
+ { "char2nr", 1, 2, f_char2nr },
+ { "cindent", 1, 1, f_cindent },
+ { "clearmatches", 0, 0, f_clearmatches },
+ { "col", 1, 1, f_col },
+ { "complete", 2, 2, f_complete },
+ { "complete_add", 1, 1, f_complete_add },
+ { "complete_check", 0, 0, f_complete_check },
+ { "confirm", 1, 4, f_confirm },
+ { "copy", 1, 1, f_copy },
+ { "cos", 1, 1, f_cos },
+ { "cosh", 1, 1, f_cosh },
+ { "count", 2, 4, f_count },
+ { "cscope_connection", 0, 3, f_cscope_connection },
+ { "cursor", 1, 3, f_cursor },
+ { "deepcopy", 1, 2, f_deepcopy },
+ { "delete", 1, 2, f_delete },
+ { "dictwatcheradd", 3, 3, f_dictwatcheradd },
+ { "dictwatcherdel", 3, 3, f_dictwatcherdel },
+ { "did_filetype", 0, 0, f_did_filetype },
+ { "diff_filler", 1, 1, f_diff_filler },
+ { "diff_hlID", 2, 2, f_diff_hlID },
+ { "empty", 1, 1, f_empty },
+ { "escape", 2, 2, f_escape },
+ { "eval", 1, 1, f_eval },
+ { "eventhandler", 0, 0, f_eventhandler },
+ { "executable", 1, 1, f_executable },
+ { "exepath", 1, 1, f_exepath },
+ { "exists", 1, 1, f_exists },
+ { "exp", 1, 1, f_exp },
+ { "expand", 1, 3, f_expand },
+ { "extend", 2, 3, f_extend },
+ { "feedkeys", 1, 2, f_feedkeys },
+ { "file_readable", 1, 1, f_filereadable }, // obsolete
+ { "filereadable", 1, 1, f_filereadable },
+ { "filewritable", 1, 1, f_filewritable },
+ { "filter", 2, 2, f_filter },
+ { "finddir", 1, 3, f_finddir },
+ { "findfile", 1, 3, f_findfile },
+ { "float2nr", 1, 1, f_float2nr },
+ { "floor", 1, 1, f_floor },
+ { "fmod", 2, 2, f_fmod },
+ { "fnameescape", 1, 1, f_fnameescape },
+ { "fnamemodify", 2, 2, f_fnamemodify },
+ { "foldclosed", 1, 1, f_foldclosed },
+ { "foldclosedend", 1, 1, f_foldclosedend },
+ { "foldlevel", 1, 1, f_foldlevel },
+ { "foldtext", 0, 0, f_foldtext },
+ { "foldtextresult", 1, 1, f_foldtextresult },
+ { "foreground", 0, 0, f_foreground },
+ { "function", 1, 1, f_function },
+ { "garbagecollect", 0, 1, f_garbagecollect },
+ { "get", 2, 3, f_get },
+ { "getbufline", 2, 3, f_getbufline },
+ { "getbufvar", 2, 3, f_getbufvar },
+ { "getchar", 0, 1, f_getchar },
+ { "getcharmod", 0, 0, f_getcharmod },
+ { "getcharsearch", 0, 0, f_getcharsearch },
+ { "getcmdline", 0, 0, f_getcmdline },
+ { "getcmdpos", 0, 0, f_getcmdpos },
+ { "getcmdtype", 0, 0, f_getcmdtype },
+ { "getcmdwintype", 0, 0, f_getcmdwintype },
+ { "getcurpos", 0, 0, f_getcurpos },
+ { "getcwd", 0, 2, f_getcwd },
+ { "getfontname", 0, 1, f_getfontname },
+ { "getfperm", 1, 1, f_getfperm },
+ { "getfsize", 1, 1, f_getfsize },
+ { "getftime", 1, 1, f_getftime },
+ { "getftype", 1, 1, f_getftype },
+ { "getline", 1, 2, f_getline },
+ { "getloclist", 1, 1, f_getqflist },
+ { "getmatches", 0, 0, f_getmatches },
+ { "getpid", 0, 0, f_getpid },
+ { "getpos", 1, 1, f_getpos },
+ { "getqflist", 0, 0, f_getqflist },
+ { "getreg", 0, 3, f_getreg },
+ { "getregtype", 0, 1, f_getregtype },
+ { "gettabvar", 2, 3, f_gettabvar },
+ { "gettabwinvar", 3, 4, f_gettabwinvar },
+ { "getwinposx", 0, 0, f_getwinposx },
+ { "getwinposy", 0, 0, f_getwinposy },
+ { "getwinvar", 2, 3, f_getwinvar },
+ { "glob", 1, 4, f_glob },
+ { "glob2regpat", 1, 1, f_glob2regpat },
+ { "globpath", 2, 5, f_globpath },
+ { "has", 1, 1, f_has },
+ { "has_key", 2, 2, f_has_key },
+ { "haslocaldir", 0, 2, f_haslocaldir },
+ { "hasmapto", 1, 3, f_hasmapto },
+ { "highlightID", 1, 1, f_hlID }, // obsolete
+ { "highlight_exists", 1, 1, f_hlexists }, // obsolete
+ { "histadd", 2, 2, f_histadd },
+ { "histdel", 1, 2, f_histdel },
+ { "histget", 1, 2, f_histget },
+ { "histnr", 1, 1, f_histnr },
+ { "hlID", 1, 1, f_hlID },
+ { "hlexists", 1, 1, f_hlexists },
+ { "hostname", 0, 0, f_hostname },
+ { "iconv", 3, 3, f_iconv },
+ { "indent", 1, 1, f_indent },
+ { "index", 2, 4, f_index },
+ { "input", 1, 3, f_input },
+ { "inputdialog", 1, 3, f_inputdialog },
+ { "inputlist", 1, 1, f_inputlist },
+ { "inputrestore", 0, 0, f_inputrestore },
+ { "inputsave", 0, 0, f_inputsave },
+ { "inputsecret", 1, 2, f_inputsecret },
+ { "insert", 2, 3, f_insert },
+ { "invert", 1, 1, f_invert },
+ { "isdirectory", 1, 1, f_isdirectory },
+ { "islocked", 1, 1, f_islocked },
+ { "items", 1, 1, f_items },
+ { "jobclose", 1, 2, f_jobclose },
+ { "jobpid", 1, 1, f_jobpid },
+ { "jobresize", 3, 3, f_jobresize },
+ { "jobsend", 2, 2, f_jobsend },
+ { "jobstart", 1, 2, f_jobstart },
+ { "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 },
+ { "libcall", 3, 3, f_libcall },
+ { "libcallnr", 3, 3, f_libcallnr },
+ { "line", 1, 1, f_line },
+ { "line2byte", 1, 1, f_line2byte },
+ { "lispindent", 1, 1, f_lispindent },
+ { "localtime", 0, 0, f_localtime },
+ { "log", 1, 1, f_log },
+ { "log10", 1, 1, f_log10 },
+ { "map", 2, 2, f_map },
+ { "maparg", 1, 4, f_maparg },
+ { "mapcheck", 1, 3, f_mapcheck },
+ { "match", 2, 4, f_match },
+ { "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 },
+ { "matchlist", 2, 4, f_matchlist },
+ { "matchstr", 2, 4, f_matchstr },
+ { "max", 1, 1, f_max },
+ { "min", 1, 1, f_min },
+ { "mkdir", 1, 3, f_mkdir },
+ { "mode", 0, 1, f_mode },
+ { "msgpackdump", 1, 1, f_msgpackdump },
+ { "msgpackparse", 1, 1, f_msgpackparse },
+ { "nextnonblank", 1, 1, f_nextnonblank },
+ { "nr2char", 1, 2, f_nr2char },
+ { "or", 2, 2, f_or },
+ { "pathshorten", 1, 1, f_pathshorten },
+ { "pow", 2, 2, f_pow },
+ { "prevnonblank", 1, 1, f_prevnonblank },
+ { "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 },
+ { "repeat", 2, 2, f_repeat },
+ { "resolve", 1, 1, f_resolve },
+ { "reverse", 1, 1, f_reverse },
+ { "round", 1, 1, f_round },
+ { "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 },
+ { "screenchar", 2, 2, f_screenchar },
+ { "screencol", 0, 0, f_screencol },
+ { "screenrow", 0, 0, f_screenrow },
+ { "search", 1, 4, f_search },
+ { "searchdecl", 1, 3, f_searchdecl },
+ { "searchpair", 3, 7, f_searchpair },
+ { "searchpairpos", 3, 7, f_searchpairpos },
+ { "searchpos", 1, 4, f_searchpos },
+ { "serverlist", 0, 0, f_serverlist },
+ { "serverstart", 0, 1, f_serverstart },
+ { "serverstop", 1, 1, f_serverstop },
+ { "setbufvar", 3, 3, f_setbufvar },
+ { "setcharsearch", 1, 1, f_setcharsearch },
+ { "setcmdpos", 1, 1, f_setcmdpos },
+ { "setline", 2, 2, f_setline },
+ { "setloclist", 2, 4, f_setloclist },
+ { "setmatches", 1, 1, f_setmatches },
+ { "setpos", 2, 2, f_setpos },
+ { "setqflist", 1, 3, f_setqflist },
+ { "setreg", 2, 3, f_setreg },
+ { "settabvar", 3, 3, f_settabvar },
+ { "settabwinvar", 4, 4, f_settabwinvar },
+ { "setwinvar", 3, 3, f_setwinvar },
+ { "sha256", 1, 1, f_sha256 },
+ { "shellescape", 1, 2, f_shellescape },
+ { "shiftwidth", 0, 0, f_shiftwidth },
+ { "simplify", 1, 1, f_simplify },
+ { "sin", 1, 1, f_sin },
+ { "sinh", 1, 1, f_sinh },
+ { "sort", 1, 3, f_sort },
+ { "soundfold", 1, 1, f_soundfold },
+ { "spellbadword", 0, 1, f_spellbadword },
+ { "spellsuggest", 1, 3, f_spellsuggest },
+ { "split", 1, 3, f_split },
+ { "sqrt", 1, 1, f_sqrt },
+ { "str2float", 1, 1, f_str2float },
+ { "str2nr", 1, 2, f_str2nr },
+ { "strchars", 1, 2, f_strchars },
+ { "strdisplaywidth", 1, 2, f_strdisplaywidth },
+ { "strftime", 1, 2, f_strftime },
+ { "stridx", 2, 3, f_stridx },
+ { "string", 1, 1, f_string },
+ { "strlen", 1, 1, f_strlen },
+ { "strpart", 2, 3, f_strpart },
+ { "strridx", 2, 3, f_strridx },
+ { "strtrans", 1, 1, f_strtrans },
+ { "strwidth", 1, 1, f_strwidth },
+ { "submatch", 1, 2, f_submatch },
+ { "substitute", 4, 4, f_substitute },
+ { "synID", 3, 3, f_synID },
+ { "synIDattr", 2, 3, f_synIDattr },
+ { "synIDtrans", 1, 1, f_synIDtrans },
+ { "synconcealed", 2, 2, f_synconcealed },
+ { "synstack", 2, 2, f_synstack },
+ { "system", 1, 2, f_system },
+ { "systemlist", 1, 3, f_systemlist },
+ { "tabpagebuflist", 0, 1, f_tabpagebuflist },
+ { "tabpagenr", 0, 1, f_tabpagenr },
+ { "tabpagewinnr", 1, 2, f_tabpagewinnr },
+ { "tagfiles", 0, 0, f_tagfiles },
+ { "taglist", 1, 1, f_taglist },
+ { "tan", 1, 1, f_tan },
+ { "tanh", 1, 1, f_tanh },
+ { "tempname", 0, 0, f_tempname },
+ { "termopen", 1, 2, f_termopen },
+ { "test", 1, 1, f_test },
+ { "tolower", 1, 1, f_tolower },
+ { "toupper", 1, 1, f_toupper },
+ { "tr", 3, 3, f_tr },
+ { "trunc", 1, 1, f_trunc },
+ { "type", 1, 1, f_type },
+ { "undofile", 1, 1, f_undofile },
+ { "undotree", 0, 0, f_undotree },
+ { "uniq", 1, 3, f_uniq },
+ { "values", 1, 1, f_values },
+ { "virtcol", 1, 1, f_virtcol },
+ { "visualmode", 0, 1, f_visualmode },
+ { "wildmenumode", 0, 0, f_wildmenumode },
+ { "winbufnr", 1, 1, f_winbufnr },
+ { "wincol", 0, 0, f_wincol },
+ { "winheight", 1, 1, f_winheight },
+ { "winline", 0, 0, f_winline },
+ { "winnr", 0, 1, f_winnr },
+ { "winrestcmd", 0, 0, f_winrestcmd },
+ { "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 },
};
@@ -7836,7 +7419,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);
}
@@ -7972,6 +7556,136 @@ static void f_argv(typval_T *argvars, typval_T *rettv)
}
}
+// Prepare "gap" for an assert error and add the sourcing position.
+static void prepare_assert_error(garray_T *gap)
+{
+ char buf[NUMBUFLEN];
+
+ ga_init(gap, 1, 100);
+ if (sourcing_name != NULL) {
+ ga_concat(gap, sourcing_name);
+ if (sourcing_lnum > 0) {
+ ga_concat(gap, (char_u *)" ");
+ }
+ }
+ if (sourcing_lnum > 0) {
+ vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)sourcing_lnum);
+ ga_concat(gap, (char_u *)buf);
+ }
+ if (sourcing_name != NULL || sourcing_lnum > 0) {
+ ga_concat(gap, (char_u *)": ");
+ }
+}
+
+// Fill "gap" with information about an assert error.
+static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv,
+ char_u *exp_str, typval_T *exp_tv,
+ typval_T *got_tv)
+{
+ char_u *tofree;
+
+ if (opt_msg_tv->v_type != VAR_UNKNOWN) {
+ 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 *) encode_tv2string(exp_tv, NULL);
+ ga_concat(gap, tofree);
+ xfree(tofree);
+ } else {
+ ga_concat(gap, exp_str);
+ }
+ tofree = (char_u *) encode_tv2string(got_tv, NULL);
+ ga_concat(gap, (char_u *)" but got ");
+ ga_concat(gap, tofree);
+ xfree(tofree);
+ }
+}
+
+// Add an assert error to v:errors.
+static void assert_error(garray_T *gap)
+{
+ struct vimvar *vp = &vimvars[VV_ERRORS];
+
+ if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) {
+ // Make sure v:errors is a list.
+ set_vim_var_list(VV_ERRORS, list_alloc());
+ }
+ list_append_string(vimvars[VV_ERRORS].vv_list,
+ gap->ga_data, gap->ga_len);
+}
+
+// "assert_equal(expected, actual[, msg])" function
+static void f_assert_equal(typval_T *argvars, typval_T *rettv)
+{
+ garray_T ga;
+
+ if (!tv_equal(&argvars[0], &argvars[1], false, false)) {
+ prepare_assert_error(&ga);
+ fill_assert_error(&ga, &argvars[2], NULL,
+ &argvars[0], &argvars[1]);
+ assert_error(&ga);
+ ga_clear(&ga);
+ }
+}
+
+/// "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);
+ }
+}
+
+// Common for assert_true() and assert_false().
+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) == 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 *)(is_true ? "True" : "False"),
+ NULL, &argvars[0]);
+ assert_error(&ga);
+ ga_clear(&ga);
+ }
+}
+
+// "assert_false(actual[, msg])" function
+static void f_assert_false(typval_T *argvars, typval_T *rettv)
+{
+ assert_bool(argvars, false);
+}
+
+// "assert_true(actual[, msg])" function
+static void f_assert_true(typval_T *argvars, typval_T *rettv)
+{
+ assert_bool(argvars, true);
+}
+
/*
* "asin()" function
*/
@@ -8615,16 +8329,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) {
@@ -8632,37 +8347,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;
}
@@ -8675,25 +8397,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
@@ -8870,7 +8618,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:
@@ -8892,9 +8640,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;
@@ -8949,7 +8700,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
@@ -8958,7 +8713,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;
@@ -9007,9 +8762,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);
@@ -9107,6 +8863,7 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action)
hashitem_T *hi2;
int todo;
bool watched = is_watched(d1);
+ 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) {
@@ -9140,6 +8897,11 @@ 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, arg_errmsg, true)
+ || var_check_ro(di1->di_flags, arg_errmsg, true)) {
+ break;
+ }
+
if (watched) {
copy_tv(&di1->di_tv, &oldtv);
}
@@ -9162,7 +8924,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;
@@ -9172,7 +8934,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);
@@ -9202,7 +8964,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) {
@@ -9348,19 +9110,21 @@ 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
- || 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
- || tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg)))
+ || (!map && tv_check_lock(d->dv_lock, arg_errmsg, true))) {
return;
+ }
} else {
EMSG2(_(e_listdictarg), ermsg);
return;
@@ -9389,17 +9153,26 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
for (hi = ht->ht_array; todo > 0; ++hi) {
if (!HASHITEM_EMPTY(hi)) {
--todo;
+
di = HI2DI(hi);
- if (tv_check_lock(di->di_tv.v_lock,
- (char_u *)_(arg_errmsg)))
+ if (map
+ && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, true)
+ || var_check_ro(di->di_flags, arg_errmsg, true))) {
break;
+ }
+
vimvars[VV_KEY].vv_str = vim_strsave(di->di_key);
int r = filter_map_one(&di->di_tv, expr, map, &rem);
clear_tv(&vimvars[VV_KEY].vv_tv);
if (r == FAIL || did_emsg)
break;
- if (!map && rem)
+ if (!map && rem) {
+ if (var_check_fixed(di->di_flags, arg_errmsg, true)
+ || var_check_ro(di->di_flags, arg_errmsg, true)) {
+ break;
+ }
dictitem_remove(d, di);
+ }
}
}
hash_unlock(ht);
@@ -9407,8 +9180,9 @@ 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 (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;
vimvars[VV_KEY].vv_nr = idx;
if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL
@@ -10047,22 +9821,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);
}
@@ -10271,6 +10153,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;
}
@@ -10305,6 +10195,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);
@@ -10377,9 +10268,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);
@@ -10393,8 +10285,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]);
@@ -10411,18 +10301,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);
}
/*
@@ -10442,9 +10327,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);
@@ -10537,11 +10423,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)
@@ -10556,12 +10442,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.
@@ -10573,8 +10463,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)
@@ -10599,10 +10491,15 @@ static void f_glob(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN) {
if (get_tv_number_chk(&argvars[1], &error))
options |= WILD_KEEP_ALL;
- if (argvars[2].v_type != VAR_UNKNOWN
- && get_tv_number_chk(&argvars[2], &error)) {
- rettv->v_type = VAR_LIST;
- rettv->vval.v_list = NULL;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ if (get_tv_number_chk(&argvars[2], &error)) {
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = NULL;
+ }
+ if (argvars[3].v_type != VAR_UNKNOWN
+ && get_tv_number_chk(&argvars[3], &error)) {
+ options |= WILD_ALLLINKS;
+ }
}
}
if (!error) {
@@ -10641,10 +10538,15 @@ static void f_globpath(typval_T *argvars, typval_T *rettv)
flags |= WILD_KEEP_ALL;
}
- if (argvars[3].v_type != VAR_UNKNOWN
- && get_tv_number_chk(&argvars[3], &error)) {
- rettv->v_type = VAR_LIST;
- rettv->vval.v_list = NULL;
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ if (get_tv_number_chk(&argvars[3], &error)) {
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = NULL;
+ }
+ if (argvars[4].v_type != VAR_UNKNOWN
+ && get_tv_number_chk(&argvars[4], &error)) {
+ flags |= WILD_ALLLINKS;
+ }
}
}
@@ -10671,15 +10573,15 @@ static void f_globpath(typval_T *argvars, typval_T *rettv)
}
}
-/*
- * "glob2regpat()" function
- */
+// "glob2regpat()" function
static void f_glob2regpat(typval_T *argvars, typval_T *rettv)
{
- char_u *pat = get_tv_string_chk(&argvars[0]);
+ char_u *pat = get_tv_string_chk(&argvars[0]); // NULL on type error
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = file_pat_to_reg_pat(pat, NULL, NULL, FALSE);
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = (pat == NULL)
+ ? NULL
+ : file_pat_to_reg_pat(pat, NULL, NULL, false);
}
/*
@@ -10779,8 +10681,10 @@ static void f_has(typval_T *argvars, typval_T *rettv)
#if !defined(UNIX)
"system", // TODO(SplinterOfChaos): This IS defined for UNIX!
#endif
+ "tablineat",
"tag_binary",
"tag_old_static",
+ "termguicolors",
"termresponse",
"textobjects",
"title",
@@ -10836,8 +10740,6 @@ static void f_has(typval_T *argvars, typval_T *rettv)
#endif
} else if (STRICMP(name, "syntax_items") == 0) {
n = syntax_present(curwin);
- } else if (STRICMP(name, "gui_running") == 0) {
- n = ui_rgb_attached();
}
}
@@ -10864,12 +10766,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;
+ }
}
/*
@@ -10902,21 +10895,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;
}
}
@@ -10931,20 +10925,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;
}
@@ -10953,20 +10948,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;
@@ -10981,11 +10977,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;
}
@@ -11292,14 +11290,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;
@@ -11493,6 +11495,31 @@ static void f_jobclose(typval_T *argvars, typval_T *rettv)
}
}
+// "jobpid(id)" function
+static void f_jobpid(typval_T *argvars, typval_T *rettv)
+{
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ if (check_restricted() || check_secure()) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_NUMBER) {
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ TerminalJobData *data = find_job(argvars[0].vval.v_number);
+ if (!data) {
+ EMSG(_(e_invjob));
+ return;
+ }
+
+ Process *proc = (Process *)&data->proc;
+ rettv->vval.v_number = proc->pid;
+}
+
// "jobsend()" function
static void f_jobsend(typval_T *argvars, typval_T *rettv)
{
@@ -11592,7 +11619,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);
@@ -11653,8 +11680,9 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv)
}
bool pty = job_opts && get_dict_number(job_opts, (uint8_t *)"pty") != 0;
+ bool detach = job_opts && get_dict_number(job_opts, (uint8_t *)"detach") != 0;
TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
- job_opts, pty);
+ job_opts, pty, detach);
Process *proc = (Process *)&data->proc;
if (pty) {
@@ -11847,6 +11875,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
*/
@@ -11888,7 +11957,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;
}
@@ -12048,8 +12120,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) {
@@ -12211,9 +12284,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);
@@ -12296,7 +12370,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;
@@ -12304,17 +12379,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
@@ -12342,12 +12431,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;
@@ -12359,7 +12460,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);
}
/*
@@ -12583,220 +12685,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;
-}
-
-#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) \
- do { \
- EMSG2(_(e_invarg2), "attempt to dump function reference"); \
- return FAIL; \
- } while (0)
-
-#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) \
- do { \
- EMSG2(_(e_invarg2), "container references itself"); \
- return FAIL; \
- } while (0)
-
-#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
@@ -12810,309 +12698,21 @@ 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];
+ int idx = 0;
for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
- if (vim_to_msgpack(lpacker, &li->li_tv) == FAIL) {
+ vim_snprintf(msgbuf, sizeof(msgbuf), (char *) msg, idx);
+ idx++;
+ 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
@@ -13130,7 +12730,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));
@@ -13144,7 +12744,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");
@@ -13652,19 +13252,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)
+ if (di == NULL) {
EMSG2(_(e_dictkey), key);
- else {
+ } 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);
@@ -13674,11 +13275,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)
@@ -13952,10 +13553,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;
@@ -13971,14 +13573,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.
@@ -14004,13 +13606,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);
@@ -14026,9 +13629,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;
@@ -14049,10 +13650,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) {
@@ -14904,33 +14510,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;
}
}
@@ -14944,8 +14581,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);
+ }
}
/*
@@ -14956,6 +14594,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv)
list_T *l;
listitem_T *li;
dict_T *d;
+ list_T *s = NULL;
rettv->vval.v_number = -1;
if (argvars[0].v_type != VAR_LIST) {
@@ -14974,7 +14613,8 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv)
return;
}
if (!(dict_find(d, (char_u *)"group", -1) != NULL
- && dict_find(d, (char_u *)"pattern", -1) != NULL
+ && (dict_find(d, (char_u *)"pattern", -1) != NULL
+ || dict_find(d, (char_u *)"pos1", -1) != NULL)
&& dict_find(d, (char_u *)"priority", -1) != NULL
&& dict_find(d, (char_u *)"id", -1) != NULL)) {
EMSG(_(e_invarg));
@@ -14986,11 +14626,51 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv)
clear_matches(curwin);
li = l->lv_first;
while (li != NULL) {
+ int i = 0;
+ char_u buf[5];
+ dictitem_T *di;
+
d = li->li_tv.vval.v_dict;
- match_add(curwin, get_dict_string(d, (char_u *)"group", FALSE),
- 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);
+ if (dict_find(d, (char_u *)"pattern", -1) == NULL) {
+ if (s == NULL) {
+ s = list_alloc();
+ if (s == NULL) {
+ return;
+ }
+ }
+
+ // match from matchaddpos()
+ for (i = 1; i < 9; ++i) {
+ snprintf((char *)buf, sizeof(buf), (char *)"pos%d", i);
+ if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) {
+ if (di->di_tv.v_type != VAR_LIST) {
+ return;
+ }
+
+ list_append_tv(s, &di->di_tv);
+ s->lv_refcount++;
+ } else {
+ break;
+ }
+ }
+ }
+
+ 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, group,
+ get_dict_string(d, (char_u *)"pattern", false),
+ priority, id, NULL, conceal);
+ } else {
+ match_add(curwin, group, NULL, priority, id, s, conceal);
+ list_unref(s);
+ s = NULL;
+ }
li = li->li_next;
}
rettv->vval.v_number = 0;
@@ -15011,25 +14691,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));
+ }
}
}
}
@@ -15039,7 +14724,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);
}
/*
@@ -15051,11 +14736,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 */
@@ -15072,17 +14757,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;
@@ -15213,26 +14898,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);
}
}
@@ -15302,6 +14993,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;
@@ -15323,9 +15016,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 *)"'";
@@ -15333,7 +15041,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) {
@@ -15342,7 +15050,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 *)"";
@@ -15451,8 +15159,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;
@@ -15465,6 +15177,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;
@@ -15486,6 +15200,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;
@@ -15590,6 +15310,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
*/
@@ -15774,31 +15509,44 @@ 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;
}
-/*
- * "str2nr()" function
- */
+// "str2nr()" function
static void f_str2nr(typval_T *argvars, typval_T *rettv)
{
int base = 10;
char_u *p;
long n;
+ int what;
if (argvars[1].v_type != VAR_UNKNOWN) {
base = get_tv_number(&argvars[1]);
- if (base != 8 && base != 10 && base != 16) {
+ if (base != 2 && base != 8 && base != 10 && base != 16) {
EMSG(_(e_invarg));
return;
}
}
p = skipwhite(get_tv_string(&argvars[0]));
- if (*p == '+')
+ if (*p == '+') {
p = skipwhite(p + 1);
- vim_str2nr(p, NULL, NULL, base == 8 ? 2 : 0, base == 16 ? 2 : 0, &n, NULL);
+ }
+ switch (base) {
+ case 2:
+ what = STR2NR_BIN + STR2NR_FORCE;
+ break;
+ case 8:
+ what = STR2NR_OCT + STR2NR_FORCE;
+ break;
+ case 16:
+ what = STR2NR_HEX + STR2NR_FORCE;
+ break;
+ default:
+ what = 0;
+ }
+ vim_str2nr(p, NULL, NULL, what, &n, NULL, 0);
rettv->vval.v_number = n;
}
@@ -15892,7 +15640,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);
}
/*
@@ -15910,13 +15658,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;
}
/*
@@ -16583,7 +16341,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
}
TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
- job_opts, true);
+ job_opts, true, false);
data->proc.pty.width = curwin->w_width;
data->proc.pty.height = curwin->w_height;
data->proc.pty.term_name = xstrdup("xterm-256color");
@@ -16615,9 +16373,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;
@@ -16803,16 +16561,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;
}
@@ -17164,6 +16939,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)
{
@@ -17391,21 +17173,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);
@@ -17476,28 +17265,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)
@@ -17512,30 +17302,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;
+ }
}
}
}
@@ -17617,14 +17421,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
@@ -17661,11 +17457,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;
}
@@ -17684,57 +17480,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;
}
/*
@@ -17742,15 +17552,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);
+ }
}
/*
@@ -17852,10 +17664,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;
@@ -17881,8 +17694,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) {
@@ -17975,25 +17792,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);
}
@@ -18031,10 +17846,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;
}
@@ -18078,9 +17894,10 @@ long get_tv_number_chk(typval_T *varp, int *denote)
EMSG(_("E703: Using a Funcref as a Number"));
break;
case VAR_STRING:
- if (varp->vval.v_string != NULL)
+ if (varp->vval.v_string != NULL) {
vim_str2nr(varp->vval.v_string, NULL, NULL,
- TRUE, TRUE, &n, NULL);
+ STR2NR_ALL, &n, NULL, 0);
+ }
return n;
case VAR_LIST:
EMSG(_("E745: Using a List as a Number"));
@@ -18088,17 +17905,57 @@ 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) /* useful for values that must be unsigned */
+ if (denote == NULL) {
+ // useful for values that must be unsigned
n = -1;
- else
- *denote = TRUE;
+ } else {
+ *denote = true;
+ }
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.
@@ -18192,8 +18049,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;
@@ -18270,6 +18130,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) {
@@ -18319,6 +18182,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)
{
@@ -18426,14 +18290,16 @@ static void vars_clear_ext(hashtab_T *ht, int free_val)
if (!HASHITEM_EMPTY(hi)) {
--todo;
- /* Free the variable. Don't remove it from the hashtab,
- * ht_array might change then. hash_clear() takes care of it
- * later. */
+ // Free the variable. Don't remove it from the hashtab,
+ // ht_array might change then. hash_clear() takes care of it
+ // later.
v = HI2DI(hi);
- if (free_val)
+ if (free_val) {
clear_tv(&v->di_tv);
- if ((v->di_flags & DI_FLAGS_FIX) == 0)
+ }
+ if (v->di_flags & DI_FLAGS_ALLOC) {
xfree(v);
+ }
}
}
hash_clear(ht);
@@ -18458,7 +18324,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);
@@ -18540,10 +18406,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)
@@ -18558,10 +18425,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);
@@ -18572,9 +18437,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 ? '/' : '?');
@@ -18582,8 +18446,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) {
@@ -18607,7 +18473,7 @@ set_var (
xfree(v);
return;
}
- v->di_flags = 0;
+ v->di_flags = DI_FLAGS_ALLOC;
}
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) {
@@ -18628,34 +18494,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;
}
/*
@@ -18703,23 +18566,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;
}
/*
@@ -18733,42 +18601,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;
}
}
@@ -18808,6 +18668,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:
@@ -18854,8 +18715,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;
@@ -18910,7 +18771,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) {
@@ -19312,7 +19173,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 (;; ) {
@@ -19442,9 +19306,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));
@@ -19536,13 +19401,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! */
@@ -19600,7 +19466,10 @@ void ex_function(exarg_T *eap)
/* insert the new function in the function list */
STRCPY(fp->uf_name, name);
- hash_add(&func_hashtab, UF2HIKEY(fp));
+ if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
+ xfree(fp);
+ goto erret;
+ }
}
fp->uf_refcount = 1;
fp->uf_args = newargs;
@@ -19707,11 +19576,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;
@@ -20495,7 +20365,7 @@ call_user_func (
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
} else {
v = xmalloc(sizeof(dictitem_T) + STRLEN(name));
- v->di_flags = DI_FLAGS_RO;
+ v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
}
STRCPY(v->di_key, name);
hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
@@ -20517,14 +20387,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) {
@@ -20544,10 +20425,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);
@@ -20600,9 +20481,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;
@@ -20638,10 +20519,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);
@@ -20912,7 +20793,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 *)"";
@@ -21491,7 +21372,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);
@@ -21586,8 +21475,13 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
return ret;
}
-static inline TerminalJobData *common_job_init(char **argv, ufunc_T *on_stdout,
- ufunc_T *on_stderr, ufunc_T *on_exit, dict_T *self, bool pty)
+static inline TerminalJobData *common_job_init(char **argv,
+ ufunc_T *on_stdout,
+ ufunc_T *on_stderr,
+ ufunc_T *on_exit,
+ dict_T *self,
+ bool pty,
+ bool detach)
{
TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData));
data->stopped = false;
@@ -21610,6 +21504,7 @@ static inline TerminalJobData *common_job_init(char **argv, ufunc_T *on_stdout,
}
proc->cb = on_process_exit;
proc->events = data->events;
+ proc->detach = detach;
return data;
}
@@ -21620,7 +21515,6 @@ static inline bool common_job_callbacks(dict_T *vopts, ufunc_T **on_stdout,
if (get_dict_callback(vopts, "on_stdout", on_stdout)
&& get_dict_callback(vopts, "on_stderr", on_stderr)
&& get_dict_callback(vopts, "on_exit", on_exit)) {
- vopts->internal_refcount++;
vopts->dv_refcount++;
return true;
}
@@ -21638,8 +21532,13 @@ static inline bool common_job_callbacks(dict_T *vopts, ufunc_T **on_stdout,
static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)
{
- data->refcount++;
Process *proc = (Process *)&data->proc;
+ if (proc->type == kProcessTypePty && proc->detach) {
+ EMSG2(_(e_invarg2), "terminal/pty job cannot be detached");
+ return false;
+ }
+
+ data->refcount++;
char *cmd = xstrdup(proc->argv[0]);
if (!process_spawn(proc)) {
EMSG2(_(e_jobspawn), cmd);
@@ -21682,7 +21581,6 @@ static inline void free_term_job_data_event(void **argv)
}
if (data->self) {
- data->self->internal_refcount--;
dict_unref(data->self);
}
queue_free(data->events);
@@ -21778,10 +21676,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) {
@@ -21804,6 +21701,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;
@@ -21811,8 +21720,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)
@@ -21933,7 +21841,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
true,
NULL);
- arguments->lv_refcount--;
+ list_unref(arguments);
// Restore caller scope information
restore_funccal(provider_caller_scope.funccalp);
provider_caller_scope = saved_provider_caller_scope;
@@ -22059,4 +21967,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 19a1bbb083..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,
@@ -111,15 +116,38 @@ enum {
VV_OPTION_NEW,
VV_OPTION_OLD,
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);
-
#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 bd50d6b829..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.
*/
@@ -101,26 +114,24 @@ struct dictitem_S {
typedef struct dictitem_S dictitem_T;
-#define DI_FLAGS_RO 1 /* "di_flags" value: read-only variable */
-#define DI_FLAGS_RO_SBX 2 /* "di_flags" value: read-only in the sandbox */
-#define DI_FLAGS_FIX 4 /* "di_flags" value: fixed variable, not allocated */
-#define DI_FLAGS_LOCK 8 /* "di_flags" value: locked variable */
+#define DI_FLAGS_RO 1 // "di_flags" value: read-only variable
+#define DI_FLAGS_RO_SBX 2 // "di_flags" value: read-only in the sandbox
+#define DI_FLAGS_FIX 4 // "di_flags" value: fixed: no :unlet or remove()
+#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 */
- int internal_refcount; // number of internal references to
- // prevent garbage collection
- 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/libuv_process.c b/src/nvim/event/libuv_process.c
index 44305c69bc..9ef3468284 100644
--- a/src/nvim/event/libuv_process.c
+++ b/src/nvim/event/libuv_process.c
@@ -21,6 +21,9 @@ bool libuv_process_spawn(LibuvProcess *uvproc)
uvproc->uvopts.args = proc->argv;
uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE
| UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
+ if (proc->detach) {
+ uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
+ }
uvproc->uvopts.exit_cb = exit_cb;
uvproc->uvopts.cwd = NULL;
uvproc->uvopts.env = NULL;
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index 0336eb880a..9bb62891c7 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -29,6 +29,7 @@
} \
} while (0)
+static bool process_is_tearing_down = false;
bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
{
@@ -112,11 +113,22 @@ bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL
{
+ process_is_tearing_down = true;
kl_iter(WatcherPtr, loop->children, current) {
Process *proc = (*current)->data;
- uv_kill(proc->pid, SIGTERM);
- proc->term_sent = true;
- process_stop(proc);
+ if (proc->detach) {
+ // Close handles to process without killing it.
+ CREATE_EVENT(loop->events, process_close_handles, 1, proc);
+ } else {
+ if (proc->type == kProcessTypeUv) {
+ uv_kill(proc->pid, SIGTERM);
+ proc->term_sent = true;
+ process_stop(proc);
+ } else { // kProcessTypePty
+ process_close_streams(proc);
+ pty_process_close_master((PtyProcess *)proc);
+ }
+ }
}
// Wait until all children exit
@@ -175,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.
@@ -303,6 +315,10 @@ static void decref(Process *proc)
static void process_close(Process *proc)
FUNC_ATTR_NONNULL_ARG(1)
{
+ if (process_is_tearing_down && proc->detach && proc->closed) {
+ // If a detached process dies while tearing down it might get closed twice.
+ return;
+ }
assert(!proc->closed);
proc->closed = true;
switch (proc->type) {
@@ -333,6 +349,7 @@ static void on_process_exit(Process *proc)
DLOG("Stopping process kill timer");
uv_timer_stop(&loop->children_kill_timer);
}
+
// Process handles are closed in the next event loop tick. This is done to
// give libuv more time to read data from the OS after the process exits(If
// process_close_streams is called with data still in the OS buffer, we lose
diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h
index 45edc46b95..e23c8ea60f 100644
--- a/src/nvim/event/process.h
+++ b/src/nvim/event/process.h
@@ -25,7 +25,7 @@ struct process {
Stream *in, *out, *err;
process_exit_cb cb;
internal_process_cb internal_exit_cb, internal_close_cb;
- bool closed, term_sent;
+ bool closed, term_sent, detach;
Queue *events;
};
@@ -48,7 +48,8 @@ static inline Process process_init(Loop *loop, ProcessType type, void *data)
.closed = false,
.term_sent = false,
.internal_close_cb = NULL,
- .internal_exit_cb = NULL
+ .internal_exit_cb = NULL,
+ .detach = false
};
}
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 3f19421a75..e8314e02e0 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);
@@ -329,9 +343,7 @@ static int sort_compare(const void *s1, const void *s2)
return result;
}
-/*
- * ":sort".
- */
+// ":sort".
void ex_sort(exarg_T *eap)
{
regmatch_T regmatch;
@@ -343,66 +355,79 @@ void ex_sort(exarg_T *eap)
char_u *p;
char_u *s;
char_u *s2;
- char_u c; /* temporary character storage */
- int unique = FALSE;
+ char_u c; // temporary character storage
+ bool unique = false;
long deleted;
colnr_T start_col;
colnr_T end_col;
- int sort_oct; /* sort on octal number */
- int sort_hex; /* sort on hex number */
+ int sort_what = 0;
- /* Sorting one line is really quick! */
- if (count <= 1)
+ // Sorting one line is really quick!
+ if (count <= 1) {
return;
+ }
- if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL)
+ if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) {
return;
+ }
sortbuf1 = NULL;
sortbuf2 = NULL;
regmatch.regprog = NULL;
sorti_T *nrs = xmalloc(count * sizeof(sorti_T));
- sort_abort = sort_ic = sort_rx = sort_nr = sort_oct = sort_hex = 0;
+ sort_abort = sort_ic = sort_rx = sort_nr = sort_flt = 0;
+ size_t format_found = 0;
for (p = eap->arg; *p != NUL; ++p) {
- if (ascii_iswhite(*p))
- ;
- else if (*p == 'i')
- sort_ic = TRUE;
- else if (*p == 'r')
- sort_rx = TRUE;
- else if (*p == 'n')
- sort_nr = 2;
- else if (*p == 'o')
- sort_oct = 2;
- else if (*p == 'x')
- sort_hex = 2;
- else if (*p == 'u')
- unique = TRUE;
- else if (*p == '"') /* comment start */
+ if (ascii_iswhite(*p)) {
+ } else if (*p == 'i') {
+ sort_ic = true;
+ } else if (*p == 'r') {
+ sort_rx = true;
+ } else if (*p == 'n') {
+ sort_nr = 1;
+ format_found++;
+ } else if (*p == 'f') {
+ sort_flt = 1;
+ format_found++;
+ } else if (*p == 'b') {
+ sort_what = STR2NR_BIN + STR2NR_FORCE;
+ format_found++;
+ } else if (*p == 'o') {
+ sort_what = STR2NR_OCT + STR2NR_FORCE;
+ format_found++;
+ } else if (*p == 'x') {
+ sort_what = STR2NR_HEX + STR2NR_FORCE;
+ format_found++;
+ } else if (*p == 'u') {
+ unique = true;
+ } else if (*p == '"') {
+ // comment start
break;
- else if (check_nextcmd(p) != NULL) {
+ } else if (check_nextcmd(p) != NULL) {
eap->nextcmd = check_nextcmd(p);
break;
} else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL) {
- s = skip_regexp(p + 1, *p, TRUE, NULL);
+ s = skip_regexp(p + 1, *p, true, NULL);
if (*s != *p) {
EMSG(_(e_invalpat));
goto sortend;
}
*s = NUL;
- /* Use last search pattern if sort pattern is empty. */
+ // Use last search pattern if sort pattern is empty.
if (s == p + 1) {
if (last_search_pat() == NULL) {
EMSG(_(e_noprevre));
goto sortend;
}
regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
- } else
+ } else {
regmatch.regprog = vim_regcomp(p + 1, RE_MAGIC);
- if (regmatch.regprog == NULL)
+ }
+ if (regmatch.regprog == NULL) {
goto sortend;
- p = s; /* continue after the regexp */
+ }
+ p = s; // continue after the regexp
regmatch.rm_ic = p_ic;
} else {
EMSG2(_(e_invarg2), p);
@@ -410,28 +435,28 @@ void ex_sort(exarg_T *eap)
}
}
- /* Can only have one of 'n', 'o' and 'x'. */
- if (sort_nr + sort_oct + sort_hex > 2) {
+ // Can only have one of 'n', 'b', 'o' and 'x'.
+ if (format_found > 1) {
EMSG(_(e_invarg));
goto sortend;
}
- /* From here on "sort_nr" is used as a flag for any number sorting. */
- sort_nr += sort_oct + sort_hex;
+ // 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
- * the lines into allocated memory.
- * When sorting on strings "start_col_nr" is the offset in the line, for
- * numbers sorting it's the number to sort on. This means the pattern
- * matching and number conversion only has to be done once per line.
- * Also get the longest line length for allocating "sortbuf".
- */
+ // Make an array with all line numbers. This avoids having to copy all
+ // the lines into allocated memory.
+ // When sorting on strings "start_col_nr" is the offset in the line, for
+ // numbers sorting it's the number to sort on. This means the pattern
+ // matching and number conversion only has to be done once per line.
+ // Also get the longest line length for allocating "sortbuf".
for (lnum = eap->line1; lnum <= eap->line2; ++lnum) {
s = ml_get(lnum);
len = (int)STRLEN(s);
- if (maxlen < len)
+ if (maxlen < len) {
maxlen = len;
+ }
start_col = 0;
end_col = len;
@@ -439,36 +464,57 @@ void ex_sort(exarg_T *eap)
if (sort_rx) {
start_col = (colnr_T)(regmatch.startp[0] - s);
end_col = (colnr_T)(regmatch.endp[0] - s);
- } else
+ } else {
start_col = (colnr_T)(regmatch.endp[0] - s);
- } else if (regmatch.regprog != NULL)
+ }
+ } else if (regmatch.regprog != NULL) {
end_col = 0;
+ }
- if (sort_nr) {
- /* Make sure vim_str2nr doesn't read any digits past the end
- * of the match, by temporarily terminating the string there */
+ 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;
c = *s2;
*s2 = NUL;
- /* Sorting on number: Store the number itself. */
+ // Sorting on number: Store the number itself.
p = s + start_col;
- if (sort_hex)
- s = skiptohex(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].start_col_nr = -MAXLNUM;
- else
- vim_str2nr(s, NULL, NULL, sort_oct, sort_hex,
- &nrs[lnum - eap->line1].start_col_nr, NULL);
+ 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 {
+ 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;
+ // Store the column to sort at.
+ 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;
@@ -479,45 +525,50 @@ void ex_sort(exarg_T *eap)
goto sortend;
}
- /* Allocate a buffer that can hold the longest line. */
+ // Allocate a buffer that can hold the longest line.
sortbuf1 = xmalloc(maxlen + 1);
sortbuf2 = xmalloc(maxlen + 1);
- /* Sort the array of line numbers. Note: can't be interrupted! */
+ // Sort the array of line numbers. Note: can't be interrupted!
qsort((void *)nrs, count, sizeof(sorti_T), sort_compare);
if (sort_abort)
goto sortend;
- /* Insert the lines in the sorted order below the last one. */
+ // Insert the lines in the sorted order below the last one.
lnum = eap->line2;
for (i = 0; i < count; ++i) {
s = ml_get(nrs[eap->forceit ? count - i - 1 : i].lnum);
if (!unique || i == 0
|| (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0) {
- if (ml_append(lnum++, s, (colnr_T)0, FALSE) == FAIL)
+ // Copy the line into a buffer, it may become invalid in
+ // ml_append(). And it's needed for "unique".
+ STRCPY(sortbuf1, s);
+ if (ml_append(lnum++, sortbuf1, (colnr_T)0, false) == FAIL) {
break;
- if (unique)
- STRCPY(sortbuf1, s);
+ }
}
fast_breakcheck();
if (got_int)
goto sortend;
}
- /* delete the original lines if appending worked */
- if (i == count)
- for (i = 0; i < count; ++i)
- ml_delete(eap->line1, FALSE);
- else
+ // delete the original lines if appending worked
+ if (i == count) {
+ for (i = 0; i < count; ++i) {
+ ml_delete(eap->line1, false);
+ }
+ } else {
count = 0;
+ }
- /* Adjust marks for deleted (or added) lines and prepare for displaying. */
+ // Adjust marks for deleted (or added) lines and prepare for displaying.
deleted = (long)(count - (lnum - eap->line2));
- if (deleted > 0)
+ if (deleted > 0) {
mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted);
- else if (deleted < 0)
+ } else if (deleted < 0) {
mark_adjust(eap->line2, MAXLNUM, -deleted, 0L);
+ }
changed_lines(eap->line1, 0, eap->line2 + 1, -deleted);
curwin->w_cursor.lnum = eap->line1;
@@ -528,8 +579,9 @@ sortend:
xfree(sortbuf1);
xfree(sortbuf2);
vim_regfree(regmatch.regprog);
- if (got_int)
+ if (got_int) {
EMSG(_(e_interr));
+ }
}
/*
@@ -598,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;
}
}
@@ -669,9 +721,17 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
{
char_u *str;
linenr_T l;
- linenr_T extra; /* Num lines added before line1 */
- linenr_T num_lines; /* Num lines moved */
- linenr_T last_line; /* Last line in file after adding new text */
+ linenr_T extra; // Num lines added before line1
+ linenr_T num_lines; // Num lines moved
+ linenr_T last_line; // Last line in file after adding new text
+
+ // Moving lines seems to corrupt the folds, delete folding info now
+ // and recreate it when finished. Don't do this for manual folding, it
+ // would delete all folds.
+ bool isFolded = hasAnyFolding(curwin) && !foldmethodIsManual(curwin);
+ if (isFolded) {
+ deleteFoldRecurse(&curwin->w_folds);
+ }
if (dest >= line1 && dest < line2) {
EMSG(_("E134: Move lines into themselves"));
@@ -756,8 +816,14 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
if (dest > last_line + 1)
dest = last_line + 1;
changed_lines(line1, 0, dest, 0L);
- } else
+ } else {
changed_lines(dest + 1, 0, line1 + num_lines, 0L);
+ }
+
+ // recreate folds
+ if (isFolded) {
+ foldUpdateAll(curwin);
+ }
return OK;
}
@@ -949,30 +1015,26 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out)
xfree(newcmd);
}
-/*
- * do_filter: filter lines through a command given by the user
- *
- * We mostly use temp files and the call_shell() routine here. This would
- * normally be done using pipes on a UNIX machine, but this is more portable
- * to non-unix machines. The call_shell() routine needs to be able
- * to deal with redirection somehow, and should handle things like looking
- * at the PATH env. variable, and adding reasonable extensions to the
- * command name given by the user. All reasonable versions of call_shell()
- * do this.
- * Alternatively, if on Unix and redirecting input or output, but not both,
- * and the 'shelltemp' option isn't set, use pipes.
- * We use input redirection if do_in is TRUE.
- * We use output redirection if do_out is TRUE.
- */
-static void
-do_filter (
+// do_filter: filter lines through a command given by the user
+//
+// We mostly use temp files and the call_shell() routine here. This would
+// normally be done using pipes on a Unix system, but this is more portable
+// to non-Unix systems. The call_shell() routine needs to be able
+// to deal with redirection somehow, and should handle things like looking
+// at the PATH env. variable, and adding reasonable extensions to the
+// command name given by the user. All reasonable versions of call_shell()
+// do this.
+// Alternatively, if on Unix and redirecting input or output, but not both,
+// and the 'shelltemp' option isn't set, use pipes.
+// We use input redirection if do_in is TRUE.
+// We use output redirection if do_out is TRUE.
+static void do_filter(
linenr_T line1,
linenr_T line2,
exarg_T *eap, /* for forced 'ff' and 'fenc' */
char_u *cmd,
int do_in,
- int do_out
-)
+ int do_out)
{
char_u *itmp = NULL;
char_u *otmp = NULL;
@@ -1295,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
@@ -1311,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;
@@ -1331,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)
@@ -1475,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);
+ }
}
/*
@@ -1555,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;
@@ -1688,7 +1755,7 @@ check_overwrite (
&& os_file_exists(ffname)) {
if (!eap->forceit && !eap->append) {
#ifdef UNIX
- /* with UNIX it is possible to open a directory */
+ // It is possible to open a directory on Unix.
if (os_isdir(ffname)) {
EMSG2(_(e_isadir2), ffname);
return FAIL;
@@ -1989,7 +2056,7 @@ do_ecmd (
char_u *free_fname = NULL;
int retval = FAIL;
long n;
- linenr_T lnum;
+ pos_T orig_pos;
linenr_T topline = 0;
int newcol = -1;
int solcol = -1;
@@ -2059,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;
@@ -2112,7 +2177,6 @@ do_ecmd (
goto theend;
if (buf->b_ml.ml_mfp == NULL) { /* no memfile yet */
oldbuf = FALSE;
- buf->b_nwindows = 0;
} else { /* existing memfile */
oldbuf = TRUE;
(void)buf_check_timestamp(buf, FALSE);
@@ -2138,7 +2202,7 @@ do_ecmd (
* Make the (new) buffer the one used by the current window.
* If the old buffer becomes unused, free it if ECMD_HIDE is FALSE.
* If the current buffer was empty and has no file name, curbuf
- * is returned by buflist_new().
+ * is returned by buflist_new(), nothing to do here.
*/
if (buf != curbuf) {
/*
@@ -2193,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;
@@ -2225,16 +2288,15 @@ do_ecmd (
}
xfree(new_name);
au_new_curbuf = NULL;
- } else
- ++curbuf->b_nwindows;
+ }
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);
}
@@ -2353,7 +2415,7 @@ do_ecmd (
* Careful: open_buffer() and apply_autocmds() may change the current
* buffer and window.
*/
- lnum = curwin->w_cursor.lnum;
+ orig_pos = curwin->w_cursor;
topline = curwin->w_topline;
if (!oldbuf) { /* need to read the file */
swap_exists_action = SEA_DIALOG;
@@ -2381,11 +2443,9 @@ do_ecmd (
}
check_arg_idx(curwin);
- /*
- * If autocommands change the cursor position or topline, we should
- * keep it.
- */
- if (curwin->w_cursor.lnum != lnum) {
+ // If autocommands change the cursor position or topline, we should keep
+ // it. Also when it moves within a line.
+ if (!equalpos(curwin->w_cursor, orig_pos)) {
newlnum = curwin->w_cursor.lnum;
newcol = curwin->w_cursor.col;
}
@@ -2456,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;
}
@@ -2861,6 +2923,8 @@ void do_sub(exarg_T *eap)
static int do_list = FALSE; /* list last line with subs. */
static int do_number = FALSE; /* list last line with line nr*/
static int do_ic = 0; /* ignore case flag */
+ int save_do_all; // remember user specified 'g' flag
+ int save_do_ask; // remember user specified 'c' flag
char_u *pat = NULL, *sub = NULL; /* init for GCC */
int delimiter;
int sublen;
@@ -2988,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;
@@ -3050,12 +3114,14 @@ void do_sub(exarg_T *eap)
break;
++cmd;
}
- if (do_count)
+ if (do_count) {
do_ask = FALSE;
+ }
- /*
- * check for a trailing count
- */
+ save_do_all = do_all;
+ save_do_ask = do_ask;
+
+ // check for a trailing count
cmd = skipwhite(cmd);
if (ascii_isdigit(*cmd)) {
i = getdigits_long(&cmd);
@@ -3754,8 +3820,12 @@ skip:
/* 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;
+ }
/*
* Give message for number of substitutions.
@@ -4744,6 +4814,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;
}
@@ -4841,16 +4912,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);
@@ -4872,7 +4940,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);
@@ -4978,7 +5046,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.
@@ -5746,13 +5816,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 b7a3505c99..04fd88cc8d 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -34,6 +34,7 @@ local ADDR_ARGUMENTS = 2
local ADDR_LOADED_BUFFERS = 3
local ADDR_BUFFERS = 4
local ADDR_TABS = 5
+local ADDR_QUICKFIX = 6
-- The following table is described in ex_cmds_defs.h file.
return {
@@ -374,6 +375,12 @@ return {
func='ex_cd',
},
{
+ command='cdo',
+ flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
+ addr_type=ADDR_QUICKFIX,
+ func='ex_listdo',
+ },
+ {
command='center',
flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
addr_type=ADDR_LINES,
@@ -391,6 +398,14 @@ return {
addr_type=ADDR_LINES,
func='ex_cfile',
},
+ -- Even though 'cfdo' is alphabetically lower than 'cfile', it is after
+ -- 'cfile' in this cmd list to support the existing ":cf" abbreviation.
+ {
+ command='cfdo',
+ flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
+ addr_type=ADDR_QUICKFIX,
+ func='ex_listdo',
+ },
{
command='cfirst',
flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG),
@@ -1010,12 +1025,6 @@ return {
func='ex_helpclose',
},
{
- command='helpfind',
- flags=bit.bor(EXTRA, NOTRLCOM),
- addr_type=ADDR_LINES,
- func='ex_ni',
- },
- {
command='helpgrep',
flags=bit.bor(EXTRA, NOTRLCOM, NEEDARG),
addr_type=ADDR_LINES,
@@ -1286,6 +1295,12 @@ return {
func='do_cscope',
},
{
+ command='ldo',
+ flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
+ addr_type=ADDR_QUICKFIX,
+ func='ex_listdo',
+ },
+ {
command='left',
flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
addr_type=ADDR_LINES,
@@ -1315,6 +1330,14 @@ return {
addr_type=ADDR_LINES,
func='ex_cfile',
},
+ -- Even though 'lfdo' is alphabetically lower than 'lfile', it is after
+ -- 'lfile' in this cmd list to support the existing ":lf" abbreviation.
+ {
+ command='lfdo',
+ flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
+ addr_type=ADDR_QUICKFIX,
+ func='ex_listdo',
+ },
{
command='lfirst',
flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG),
@@ -2552,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 87a6283310..df387f9a60 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -6,6 +6,7 @@
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
+#include <fcntl.h>
#include "nvim/vim.h"
#include "nvim/ascii.h"
@@ -56,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};
@@ -80,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;
/*
@@ -92,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)])
@@ -271,7 +272,7 @@ void do_debug(char_u *cmd)
xfree(cmdline);
}
- lines_left = Rows - 1;
+ lines_left = (int)(Rows - 1);
}
xfree(cmdline);
@@ -280,7 +281,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;
@@ -391,12 +392,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};
@@ -431,14 +432,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;
@@ -451,16 +450,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)
@@ -562,13 +560,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;
@@ -601,11 +600,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. */
@@ -696,14 +697,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)) {
@@ -809,8 +809,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[] = {
@@ -891,7 +891,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;
@@ -948,7 +948,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;
}
@@ -1241,28 +1241,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);
@@ -1346,9 +1349,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);
@@ -1519,7 +1523,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;
@@ -1536,9 +1540,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;
}
@@ -1701,10 +1703,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);
}
@@ -1843,22 +1846,25 @@ 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;
+ (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;
+ }
}
} else if (*eap->arg == NUL)
EMSG(_(e_argreq));
@@ -1868,7 +1874,7 @@ void ex_argdelete(exarg_T *eap)
}
/*
- * ":argdo", ":windo", ":bufdo", ":tabdo"
+ * ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
*/
void ex_listdo(exarg_T *eap)
{
@@ -1879,7 +1885,6 @@ void ex_listdo(exarg_T *eap)
char_u *save_ei = NULL;
char_u *p_shm_save;
-
if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo)
/* Don't do syntax HL autocommands. Skipping the syntax file is a
* great speed improvement. */
@@ -1909,12 +1914,15 @@ void ex_listdo(exarg_T *eap)
}
break;
case CMD_argdo:
- i = eap->line1 - 1;
+ i = (int)eap->line1 - 1;
break;
default:
break;
}
+
buf_T *buf = curbuf;
+ size_t qf_size = 0;
+
/* set pcmark now */
if (eap->cmdidx == CMD_bufdo) {
/* Advance to the first listed buffer after "eap->line1". */
@@ -1929,6 +1937,23 @@ 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) {
+ qf_size = qf_get_size(eap);
+ assert(eap->line1 >= 0);
+ if (qf_size == 0 || (size_t)eap->line1 > qf_size) {
+ buf = NULL;
+ } else {
+ ex_cc(eap);
+
+ buf = curbuf;
+ i = (int)eap->line1 - 1;
+ if (eap->addr_count <= 0) {
+ // Default to all quickfix/location list entries.
+ assert(qf_size < MAXLNUM);
+ eap->line2 = (linenr_T)qf_size;
+ }
+ }
} else {
setpcmark();
}
@@ -2009,9 +2034,27 @@ void ex_listdo(exarg_T *eap)
set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
- /* If autocommands took us elsewhere, quit here */
- if (curbuf->b_fnum != next_fnum)
+ // If autocommands took us elsewhere, quit here.
+ if (curbuf->b_fnum != next_fnum) {
break;
+ }
+ }
+
+ 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;
+ }
+
+ size_t qf_idx = qf_get_cur_idx(eap);
+
+ ex_cnext(eap);
+
+ // If jumping to the next quickfix entry fails, quit here.
+ if (qf_get_cur_idx(eap) == qf_idx) {
+ break;
+ }
}
if (eap->cmdidx == CMD_windo) {
@@ -2061,7 +2104,7 @@ 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);
@@ -2330,8 +2373,9 @@ static FILE *fopen_noinh_readbin(char *filename)
# 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
@@ -2534,7 +2578,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;
@@ -3125,27 +3169,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);
}
@@ -3349,8 +3396,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);
}
@@ -3366,16 +3413,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_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 10d2eb688e..f46d1e6d47 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -72,6 +72,7 @@
#define ADDR_LOADED_BUFFERS 3
#define ADDR_BUFFERS 4
#define ADDR_TABS 5
+#define ADDR_QUICKFIX 6
typedef struct exarg exarg_T;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 59bda9345e..870284a0f7 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;
@@ -1531,9 +1530,12 @@ static char_u * do_one_cmd(char_u **cmdlinep,
lnum = CURRENT_TAB_NR;
ea.line2 = lnum;
break;
+ case ADDR_QUICKFIX:
+ ea.line2 = qf_get_cur_valid_idx(&ea);
+ break;
}
ea.cmd = skipwhite(ea.cmd);
- lnum = get_address(&ea.cmd, ea.addr_type, ea.skip, ea.addr_count == 0);
+ lnum = get_address(&ea, &ea.cmd, ea.addr_type, ea.skip, ea.addr_count == 0);
if (ea.cmd == NULL) /* error detected */
goto doend;
if (lnum == MAXLNUM) {
@@ -1582,6 +1584,13 @@ static char_u * do_one_cmd(char_u **cmdlinep,
ea.line2 = ARGCOUNT;
}
break;
+ case ADDR_QUICKFIX:
+ ea.line1 = 1;
+ ea.line2 = qf_get_size(&ea);
+ if (ea.line2 == 0) {
+ ea.line2 = 1;
+ }
+ break;
}
++ea.addr_count;
}
@@ -1645,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;
@@ -1684,7 +1692,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
// autocommands defined, trigger the matching autocommands.
if (p != NULL && ea.cmdidx == CMD_SIZE && !ea.skip
&& ASCII_ISUPPER(*ea.cmd)
- && has_cmdundefined()) {
+ && has_event(EVENT_CMDUNDEFINED)) {
p = ea.cmd;
while (ASCII_ISALNUM(*p)) {
++p;
@@ -1692,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) {
@@ -1817,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);
}
@@ -1962,6 +1971,12 @@ static char_u * do_one_cmd(char_u **cmdlinep,
ea.line2 = ARGCOUNT;
}
break;
+ case ADDR_QUICKFIX:
+ ea.line2 = qf_get_size(&ea);
+ if (ea.line2 == 0) {
+ ea.line2 = 1;
+ }
+ break;
}
}
@@ -2332,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'
@@ -2660,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);
@@ -2683,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++;
+ }
+ }
}
/*
@@ -2794,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;
@@ -2929,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;
@@ -2945,6 +2978,8 @@ set_one_cmd_context (
case CMD_botright:
case CMD_browse:
case CMD_bufdo:
+ case CMD_cdo:
+ case CMD_cfdo:
case CMD_confirm:
case CMD_debug:
case CMD_folddoclosed:
@@ -2954,7 +2989,9 @@ set_one_cmd_context (
case CMD_keepjumps:
case CMD_keepmarks:
case CMD_keeppatterns:
+ case CMD_ldo:
case CMD_leftabove:
+ case CMD_lfdo:
case CMD_lockmarks:
case CMD_noautocmd:
case CMD_noswapfile:
@@ -3367,7 +3404,8 @@ skip_range (
*
* Return MAXLNUM when no Ex address was found.
*/
-static linenr_T get_address(char_u **ptr,
+static linenr_T get_address(exarg_T *eap,
+ char_u **ptr,
int addr_type, // flag: one of ADDR_LINES, ...
int skip, // only skip the address, don't use it
int to_other_file // flag: may jump to other file
@@ -3405,6 +3443,9 @@ static linenr_T get_address(char_u **ptr,
case ADDR_TABS:
lnum = CURRENT_TAB_NR;
break;
+ case ADDR_QUICKFIX:
+ lnum = qf_get_cur_valid_idx(eap);
+ break;
}
break;
@@ -3436,6 +3477,12 @@ static linenr_T get_address(char_u **ptr,
case ADDR_TABS:
lnum = LAST_TAB_NR;
break;
+ case ADDR_QUICKFIX:
+ lnum = qf_get_size(eap);
+ if (lnum == 0) {
+ lnum = 1;
+ }
+ break;
}
break;
@@ -3578,6 +3625,9 @@ static linenr_T get_address(char_u **ptr,
case ADDR_TABS:
lnum = CURRENT_TAB_NR;
break;
+ case ADDR_QUICKFIX:
+ lnum = qf_get_cur_valid_idx(eap);
+ break;
}
}
@@ -3702,6 +3752,12 @@ static char_u *invalid_range(exarg_T *eap)
return (char_u *)_(e_invrange);
}
break;
+ case ADDR_QUICKFIX:
+ assert(eap->line2 >= 0);
+ if (eap->line2 != 1 && (size_t)eap->line2 > qf_get_size(eap)) {
+ return (char_u *)_(e_invrange);
+ }
+ break;
}
}
return NULL;
@@ -4268,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);
}
/*
@@ -4276,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)
@@ -4501,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);
@@ -4589,6 +4646,7 @@ static struct {
{ADDR_TABS, "tabs"},
{ADDR_BUFFERS, "buffers"},
{ADDR_WINDOWS, "windows"},
+ {ADDR_QUICKFIX, "quickfix"},
{-1, NULL}
};
@@ -4714,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++] = ' ';
@@ -5446,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) {
@@ -5457,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;
@@ -5595,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;
+ }
/*
@@ -5610,14 +5671,19 @@ 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 {
- if (only_one_window()) {
- // quit last window
+ // quit last window
+ // Note: only_one_window() returns true, even so a help window is
+ // still open. In that case only quit, if no address has been
+ // specified. Example:
+ // :h|wincmd w|1q - don't quit
+ // :h|wincmd w|q - quit
+ if (only_one_window() && (firstwin == lastwin || eap->addr_count == 0)) {
getout(0);
}
/* close window; may free buffer */
@@ -5657,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();
}
@@ -5944,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));
}
}
@@ -6232,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);
@@ -6305,7 +6371,7 @@ static void ex_tabnext(exarg_T *eap)
*/
static void ex_tabmove(exarg_T *eap)
{
- int tab_number = 9999;
+ int tab_number;
if (eap->arg && *eap->arg != NUL) {
char_u *p = eap->arg;
@@ -6321,17 +6387,35 @@ static void ex_tabmove(exarg_T *eap)
} else
p = eap->arg;
- if (p == skipdigits(p)) {
- /* No numbers as argument. */
- eap->errmsg = e_invarg;
- return;
+ if (relative == 0) {
+ if (STRCMP(p, "$") == 0) {
+ tab_number = LAST_TAB_NR;
+ } else if (p == skipdigits(p)) {
+ // No numbers as argument.
+ eap->errmsg = e_invarg;
+ return;
+ } else {
+ tab_number = getdigits(&p);
+ }
+ } else {
+ if (*p != NUL) {
+ tab_number = getdigits(&p);
+ } else {
+ tab_number = 1;
+ }
+ tab_number = tab_number * relative + tabpage_index(curtab);
+ if (relative == -1) {
+ --tab_number;
+ }
}
-
- tab_number = getdigits_int(&p);
- if (relative != 0)
- tab_number = tab_number * relative + tabpage_index(curtab) - 1; ;
- } else if (eap->addr_count != 0)
+ } else if (eap->addr_count != 0) {
tab_number = eap->line2;
+ if (**eap->cmdlinep == '-') {
+ --tab_number;
+ }
+ } else {
+ tab_number = LAST_TAB_NR;
+ }
tabpage_move(tab_number);
}
@@ -6740,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;
@@ -6803,17 +6906,32 @@ void ex_cd(exarg_T *eap)
prev_dir = NULL;
#if defined(UNIX)
- /* for UNIX ":cd" means: go to home directory */
+ // On Unix ":cd" means: go to home directory.
if (*new_dir == NUL) {
- /* use NameBuff for home directory name */
+ // Use NameBuff for home directory name.
expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
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)
@@ -6957,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);
@@ -7013,9 +7131,7 @@ static void ex_put(exarg_T *eap)
*/
static void ex_copymove(exarg_T *eap)
{
- long n;
-
- n = get_address(&eap->arg, eap->addr_type, FALSE, FALSE);
+ long n = get_address(eap, &eap->arg, eap->addr_type, false, false);
if (eap->arg == NULL) { /* error detected */
eap->nextcmd = NULL;
return;
@@ -7349,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) {
@@ -7363,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;
@@ -7432,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)
@@ -7478,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
@@ -8219,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++;
}
}
@@ -8307,8 +8431,7 @@ makeopens (
{
int only_save_windows = TRUE;
int nr;
- int cnr = 1;
- int restore_size = TRUE;
+ int restore_size = true;
win_T *wp;
char_u *sname;
win_T *edited_win = NULL;
@@ -8425,7 +8548,8 @@ makeopens (
tab_firstwin = firstwin; /* first window in tab page "tabnr" */
tab_topframe = topframe;
for (tabnr = 1;; ++tabnr) {
- int need_tabnew = FALSE;
+ int need_tabnew = false;
+ int cnr = 1;
if ((ssop_flags & SSOP_TABPAGES)) {
tabpage_T *tp = find_tabpage(tabnr);
@@ -8704,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;
}
/*
@@ -9066,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;
}
@@ -9152,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}"
@@ -9168,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;
}
@@ -9196,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') {
@@ -9214,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}"
*/
@@ -9266,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;
}
@@ -9358,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 = "\"";
}
@@ -9373,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 6d81f3680a..a4e5a4dcd7 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);
@@ -596,7 +598,7 @@ static int command_line_execute(VimState *state, int key)
}
if (s->j > 0) {
- // TODO(tarruda): this is only for DOS/UNIX systems - need to put in
+ // TODO(tarruda): this is only for DOS/Unix systems - need to put in
// machine-specific stuff here and in upseg init
cmdline_del(s->j);
put_on_cmdline(upseg + 1, 3, false);
@@ -622,8 +624,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 +1132,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 +1270,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 +2426,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 +2956,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 +2995,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
@@ -3701,6 +3718,9 @@ ExpandFromContext (
flags |= EW_KEEPALL;
if (options & WILD_SILENT)
flags |= EW_SILENT;
+ if (options & WILD_ALLLINKS) {
+ flags |= EW_ALLLINKS;
+ }
if (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_DIRECTORIES
@@ -3963,6 +3983,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 */
@@ -3971,7 +3992,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. */
@@ -3991,12 +4012,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)
@@ -4254,20 +4287,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;
}
/*
@@ -4436,28 +4482,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 */
@@ -4478,8 +4534,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;
@@ -4767,35 +4825,40 @@ int del_history_idx(int histype, int idx)
return TRUE;
}
-/*
- * Get indices "num1,num2" that specify a range within a list (not a range of
- * text lines in a buffer!) from a string. Used for ":history" and ":clist".
- * Returns OK if parsed successfully, otherwise FAIL.
- */
+/// Get indices that specify a range within a list (not a range of text lines
+/// in a buffer!) from a string. Used for ":history" and ":clist".
+///
+/// @param str string to parse range from
+/// @param num1 from
+/// @param num2 to
+///
+/// @return OK if parsed successfully, otherwise FAIL.
int get_list_range(char_u **str, int *num1, int *num2)
{
int len;
- int first = FALSE;
+ int first = false;
long num;
*str = skipwhite(*str);
- if (**str == '-' || ascii_isdigit(**str)) { /* parse "from" part of range */
- vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL);
+ if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range
+ vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0);
*str += len;
*num1 = (int)num;
- first = TRUE;
+ first = true;
}
*str = skipwhite(*str);
- if (**str == ',') { /* parse "to" part of range */
+ if (**str == ',') { // parse "to" part of range
*str = skipwhite(*str + 1);
- vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL);
+ vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0);
if (len > 0) {
*num2 = (int)num;
*str = skipwhite(*str + len);
- } else if (!first) /* no number given at all */
+ } else if (!first) { // no number given at all
return FAIL;
- } else if (first) /* only one number given */
+ }
+ } else if (first) { // only one number given
*num2 = *num1;
+ }
return OK;
}
@@ -4824,23 +4887,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;
@@ -4948,7 +5008,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;
@@ -4997,7 +5056,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);
@@ -5012,7 +5071,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;
@@ -5128,6 +5187,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 c537d681c6..24eebdc303 100644
--- a/src/nvim/ex_getln.h
+++ b/src/nvim/ex_getln.h
@@ -14,23 +14,26 @@
#define WILD_LONGEST 7
#define WILD_ALL_KEEP 8
-#define WILD_LIST_NOTFOUND 1
-#define WILD_HOME_REPLACE 2
-#define WILD_USE_NL 4
-#define WILD_NO_BEEP 8
-#define WILD_ADD_SLASH 16
-#define WILD_KEEP_ALL 32
-#define WILD_SILENT 64
-#define WILD_ESCAPE 128
-#define WILD_ICASE 256
+#define WILD_LIST_NOTFOUND 0x01
+#define WILD_HOME_REPLACE 0x02
+#define WILD_USE_NL 0x04
+#define WILD_NO_BEEP 0x08
+#define WILD_ADD_SLASH 0x10
+#define WILD_KEEP_ALL 0x20
+#define WILD_SILENT 0x40
+#define WILD_ESCAPE 0x80
+#define WILD_ICASE 0x100
+#define WILD_ALLLINKS 0x200
/// 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 4f345158cf..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;
+ }
}
}
@@ -1340,6 +1337,7 @@ void free_findfile(void)
*
* options:
* FNAME_MESS give error message when not found
+ * FNAME_UNESC unescape backslashes
*
* Uses NameBuff[]!
*
@@ -1385,6 +1383,14 @@ find_file_in_path_option (
xfree(ff_file_to_find);
ff_file_to_find = vim_strsave(NameBuff);
+ if (options & FNAME_UNESC) {
+ // Change all "\ " to " ".
+ for (ptr = ff_file_to_find; *ptr != NUL; ++ptr) {
+ if (ptr[0] == '\\' && ptr[1] == ' ') {
+ memmove(ptr, ptr + 1, STRLEN(ptr));
+ }
+ }
+ }
}
rel_to_curdir = (ff_file_to_find[0] == '.'
@@ -1394,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 bc5b08ef24..db1469db97 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -7,6 +7,7 @@
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
+#include <fcntl.h>
#include "nvim/vim.h"
#include "nvim/ascii.h"
@@ -44,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"
@@ -366,14 +366,13 @@ readfile (
/* don't display the file info for another buffer now */
need_fileinfo = FALSE;
- /*
- * For Unix: Use the short file name whenever possible.
- * Avoids problems with networks and when directory names are changed.
- * Don't do this for MS-DOS, a "cd" in a sub-shell may have moved us to
- * another directory, which we don't detect.
- */
- if (sfname == NULL)
+ // For Unix: Use the short file name whenever possible.
+ // Avoids problems with networks and when directory names are changed.
+ // Don't do this for Windows, a "cd" in a sub-shell may have moved us to
+ // another directory, which we don't detect.
+ if (sfname == NULL) {
sfname = fname;
+ }
#if defined(UNIX)
fname = sfname;
#endif
@@ -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
@@ -1543,6 +1543,11 @@ rewind_retry:
if (fileformat == EOL_UNKNOWN) {
/* First try finding a NL, for Dos and Unix */
if (try_dos || try_unix) {
+ // Reset the carriage return counter.
+ if (try_mac) {
+ try_mac = 1;
+ }
+
for (p = ptr; p < ptr + size; ++p) {
if (*p == NL) {
if (!try_unix
@@ -1551,6 +1556,8 @@ rewind_retry:
else
fileformat = EOL_UNIX;
break;
+ } else if (*p == CAR && try_mac) {
+ try_mac++;
}
}
@@ -1571,6 +1578,10 @@ rewind_retry:
if (try_mac > try_unix)
fileformat = EOL_MAC;
}
+ } else if (fileformat == EOL_UNKNOWN && try_mac == 1) {
+ // Looking for CR but found no end-of-line markers at all:
+ // use the default format.
+ fileformat = default_fileformat();
}
}
@@ -1867,7 +1878,8 @@ failed:
}
if (msg_add_fileformat(fileformat))
c = TRUE;
- msg_add_lines(c, (long)linecnt, filesize);
+
+ msg_add_lines(c, (long)linecnt, filesize);
xfree(keep_msg);
keep_msg = NULL;
@@ -1922,10 +1934,10 @@ failed:
check_marks_read();
/*
- * Trick: We remember if the last line of the read didn't have
- * an eol even when 'binary' is off, for when writing it again with
- * 'binary' on. This is required for
- * ":autocmd FileReadPost *.gz set bin|'[,']!gunzip" to work.
+ * We remember if the last line of the read didn't have
+ * an eol even when 'binary' is off, to support turning 'fixeol' off,
+ * or writing the read again with 'binary' on. The latter is required
+ * for ":autocmd FileReadPost *.gz set bin|'[,']!gunzip" to work.
*/
curbuf->b_no_eol_lnum = read_no_eol_lnum;
@@ -1980,13 +1992,14 @@ failed:
}
#ifdef OPEN_CHR_FILES
-/*
- * Returns TRUE if the file name argument is of the form "/dev/fd/\d\+",
- * which is the name of files used for process substitution output by
- * some shells on some operating systems, e.g., bash on SunOS.
- * Do not accept "/dev/fd/[012]", opening these may hang Vim.
- */
-static int is_dev_fd_file(char_u *fname)
+/// Returns true if the file name argument is of the form "/dev/fd/\d\+",
+/// which is the name of files used for process substitution output by
+/// some shells on some operating systems, e.g., bash on SunOS.
+/// Do not accept "/dev/fd/[012]", opening these may hang Vim.
+///
+/// @param fname file name to check
+static bool is_dev_fd_file(char_u *fname)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
return STRNCMP(fname, "/dev/fd/", 8) == 0
&& ascii_isdigit(fname[8])
@@ -2126,9 +2139,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'");
}
@@ -2341,13 +2355,12 @@ buf_write (
if (sfname == NULL)
sfname = fname;
- /*
- * For Unix: Use the short file name whenever possible.
- * Avoids problems with networks and when directory names are changed.
- * Don't do this for MS-DOS, a "cd" in a sub-shell may have moved us to
- * another directory, which we don't detect
- */
- ffname = fname; /* remember full fname */
+
+ // For Unix: Use the short file name whenever possible.
+ // Avoids problems with networks and when directory names are changed.
+ // Don't do this for Windows, a "cd" in a sub-shell may have moved us to
+ // another directory, which we don't detect.
+ ffname = fname; // remember full fname
#ifdef UNIX
fname = sfname;
#endif
@@ -2564,7 +2577,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;
@@ -2576,11 +2589,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");
@@ -2676,7 +2689,6 @@ buf_write (
} else if ((bkc & BKC_AUTO)) { /* "auto" */
int i;
-# ifdef UNIX
/*
* Don't rename the file when:
* - it's a hard link
@@ -2687,9 +2699,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.
@@ -3310,7 +3320,7 @@ restore_backup:
/* write failed or last line has no EOL: stop here */
if (end == 0
|| (lnum == end
- && write_bin
+ && (write_bin || !buf->b_p_fixeol)
&& (lnum == buf->b_no_eol_lnum
|| (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol)))) {
++lnum; /* written the line, count it */
@@ -3357,16 +3367,16 @@ restore_backup:
nchars += len;
}
-#if defined(UNIX) && defined(HAVE_FSYNC)
- /* On many journalling file systems there is a bug that causes both the
- * original and the backup file to be lost when halting the system right
- * after writing the file. That's because only the meta-data is
- * journalled. Syncing the file slows down the system, but assures it has
- * been written to disk and we don't lose it.
- * For a device do try the fsync() but don't complain if it does not work
- * (could be a pipe).
- * If the 'fsync' option is FALSE, don't fsync(). Useful for laptops. */
- if (p_fs && fsync(fd) != 0 && !device) {
+#if defined(UNIX)
+ // On many journalling file systems there is a bug that causes both the
+ // original and the backup file to be lost when halting the system right
+ // after writing the file. That's because only the meta-data is
+ // journalled. Syncing the file slows down the system, but assures it has
+ // been written to disk and we don't lose it.
+ // For a device do try the fsync() but don't complain if it does not work
+ // (could be a pipe).
+ // If the 'fsync' option is FALSE, don't fsync(). Useful for laptops.
+ if (p_fs && os_fsync(fd) != 0 && !device) {
errmsg = (char_u *)_("E667: Fsync failed");
end = 0;
}
@@ -3423,9 +3433,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;
}
}
@@ -3795,29 +3805,30 @@ void msg_add_fname(buf_T *buf, char_u *fname)
STRCAT(IObuff, "\" ");
}
-/*
- * Append message for text mode to IObuff.
- * Return TRUE if something appended.
- */
-static int msg_add_fileformat(int eol_type)
+/// Append message for text mode to IObuff.
+///
+/// @param eol_type line ending type
+///
+/// @return true if something was appended.
+static bool msg_add_fileformat(int eol_type)
{
#ifndef USE_CRNL
if (eol_type == EOL_DOS) {
STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[dos]") : _("[dos format]"));
- return TRUE;
+ return true;
}
#endif
if (eol_type == EOL_MAC) {
STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[mac]") : _("[mac format]"));
- return TRUE;
+ return true;
}
#ifdef USE_CRNL
if (eol_type == EOL_UNIX) {
STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[unix]") : _("[unix format]"));
- return TRUE;
+ return true;
}
#endif
- return FALSE;
+ return false;
}
/*
@@ -3881,7 +3892,11 @@ static int check_mtime(buf_T *buf, FileInfo *file_info)
return OK;
}
-static int time_differs(long t1, long t2)
+/// Return true if the times differ
+///
+/// @param t1 first time
+/// @param t2 second time
+static bool time_differs(long t1, long t2) FUNC_ATTR_CONST
{
#if defined(__linux__) || defined(MSWIN)
/* On a FAT filesystem, esp. under Linux, there are only 5 bits to store
@@ -4070,19 +4085,17 @@ static int buf_write_bytes(struct bw_info *ip)
return (wlen < len) ? FAIL : OK;
}
-/*
- * Convert a Unicode character to bytes.
- * Return TRUE for an error, FALSE when it's OK.
- */
-static int
-ucs2bytes (
- unsigned c, /* in: character */
- char_u **pp, /* in/out: pointer to result */
- int flags /* FIO_ flags */
-)
+/// Convert a Unicode character to bytes.
+///
+/// @param c character to convert
+/// @param[in,out] pp pointer to store the result at
+/// @param flags FIO_ flags that specify which encoding to use
+///
+/// @return true for an error, false when it's OK.
+static bool ucs2bytes(unsigned c, char_u **pp, int flags) FUNC_ATTR_NONNULL_ALL
{
char_u *p = *pp;
- int error = FALSE;
+ bool error = false;
int cc;
@@ -4104,8 +4117,9 @@ ucs2bytes (
/* Make two words, ten bits of the character in each. First
* word is 0xd800 - 0xdbff, second one 0xdc00 - 0xdfff */
c -= 0x10000;
- if (c >= 0x100000)
- error = TRUE;
+ if (c >= 0x100000) {
+ error = true;
+ }
cc = ((c >> 10) & 0x3ff) + 0xd800;
if (flags & FIO_ENDIAN_L) {
*p++ = cc;
@@ -4115,8 +4129,9 @@ ucs2bytes (
*p++ = cc;
}
c = (c & 0x3ff) + 0xdc00;
- } else
- error = TRUE;
+ } else {
+ error = true;
+ }
}
if (flags & FIO_ENDIAN_L) {
*p++ = c;
@@ -4127,7 +4142,7 @@ ucs2bytes (
}
} else { /* Latin1 */
if (c >= 0x100) {
- error = TRUE;
+ error = true;
*p++ = 0xBF;
} else
*p++ = c;
@@ -4137,11 +4152,14 @@ ucs2bytes (
return error;
}
-/*
- * Return TRUE if file encoding "fenc" requires conversion from or to
- * 'encoding'.
- */
-static int need_conversion(char_u *fenc)
+/// Return true if file encoding "fenc" requires conversion from or to
+/// 'encoding'.
+///
+/// @param fenc file encoding to check
+///
+/// @return true if conversion is required
+static bool need_conversion(const char_u *fenc)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
int same_encoding;
int enc_flags;
@@ -4168,19 +4186,19 @@ static int need_conversion(char_u *fenc)
return !(enc_utf8 && fenc_flags == FIO_UTF8);
}
-/*
- * Check "ptr" for a unicode encoding and return the FIO_ flags needed for the
- * internal conversion.
- * if "ptr" is an empty string, use 'encoding'.
- */
-static int get_fio_flags(char_u *ptr)
+/// Return the FIO_ flags needed for the internal conversion if 'name' was
+/// unicode or latin1, otherwise 0. If "name" is an empty string,
+/// use 'encoding'.
+///
+/// @param name string to check for encoding
+static int get_fio_flags(const char_u *name)
{
int prop;
- if (*ptr == NUL)
- ptr = p_enc;
-
- prop = enc_canon_props(ptr);
+ if (*name == NUL) {
+ name = p_enc;
+ }
+ prop = enc_canon_props(name);
if (prop & ENC_UNICODE) {
if (prop & ENC_2BYTE) {
if (prop & ENC_ENDIAN_L)
@@ -4352,8 +4370,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;
}
@@ -4412,11 +4430,15 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)
return retval;
}
-/*
- * Like fgets(), but if the file line is too long, it is truncated and the
- * rest of the line is thrown away. Returns TRUE for end-of-file.
- */
-int vim_fgets(char_u *buf, int size, FILE *fp)
+/// Like fgets(), but if the file line is too long, it is truncated and the
+/// rest of the line is thrown away.
+///
+/// @param[out] buf buffer to fill
+/// @param size size of the buffer
+/// @param fp file to read from
+///
+/// @return true for end-of-file.
+bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL
{
char *eof;
#define FGETS_SIZE 200
@@ -4716,7 +4738,6 @@ buf_check_timestamp (
{
int retval = 0;
char_u *path;
- char_u *tbuf;
char *mesg = NULL;
char *mesg2 = "";
int helpmesg = FALSE;
@@ -4786,19 +4807,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"));
@@ -4852,35 +4871,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) {
@@ -5074,22 +5097,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.
@@ -5315,11 +5483,13 @@ static void au_del_group(char_u *name)
}
}
-/*
- * Find the ID of an autocmd group name.
- * Return it's ID. Returns AUGROUP_ERROR (< 0) for error.
- */
-static int au_find_group(char_u *name)
+/// Find the ID of an autocmd group name.
+///
+/// @param name augroup name
+///
+/// @return the ID or AUGROUP_ERROR (< 0) for error.
+static int au_find_group(const char_u *name)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
for (int i = 0; i < augroups.ga_len; ++i) {
if (AUGROUP_NAME(i) != NULL && STRCMP(AUGROUP_NAME(i), name) == 0) {
@@ -5329,10 +5499,11 @@ static int au_find_group(char_u *name)
return AUGROUP_ERROR;
}
-/*
- * Return TRUE if augroup "name" exists.
- */
-int au_has_group(char_u *name)
+/// Return true if augroup "name" exists.
+///
+/// @param name augroup name
+bool au_has_group(const char_u *name)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return au_find_group(name) != AUGROUP_ERROR;
}
@@ -5449,21 +5620,24 @@ find_end_event (
return pat;
}
-/*
- * Return TRUE if "event" is included in 'eventignore'.
- */
-static int event_ignored(event_T event)
+/// Return true if "event" is included in 'eventignore'.
+///
+/// @param event event to check
+static bool event_ignored(event_T event)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- char_u *p = p_ei;
+ char_u *p = p_ei;
while (*p != NUL) {
- if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ','))
- return TRUE;
- if (event_name2nr(p, &p) == event)
- return TRUE;
+ if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) {
+ return true;
+ }
+ if (event_name2nr(p, &p) == event) {
+ return true;
+ }
}
- return FALSE;
+ return false;
}
/*
@@ -5980,18 +6154,19 @@ void ex_doautoall(exarg_T *eap)
check_cursor(); /* just in case lines got deleted */
}
-/*
- * Check *argp for <nomodeline>. When it is present return FALSE, otherwise
- * return TRUE and advance *argp to after it.
- * Thus return TRUE when do_modelines() should be called.
- */
-int check_nomodeline(char_u **argp)
+/// Check *argp for <nomodeline>. When it is present return false, otherwise
+/// return true and advance *argp to after it. Thus do_modelines() should be
+/// called when true is returned.
+///
+/// @param[in,out] argp argument string
+bool check_nomodeline(char_u **argp)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
if (STRNCMP(*argp, "<nomodeline>", 12) == 0) {
*argp = skipwhite(*argp + 12);
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
/*
@@ -6168,76 +6343,78 @@ win_found:
static int autocmd_nested = FALSE;
-/*
- * Execute autocommands for "event" and file name "fname".
- * Return TRUE if some commands were executed.
- */
-int
-apply_autocmds (
- event_T event,
- char_u *fname, /* NULL or empty means use actual file name */
- char_u *fname_io, /* fname to use for <afile> on cmdline */
- int force, /* when TRUE, ignore autocmd_busy */
- buf_T *buf /* buffer for <abuf> */
-)
+/// Execute autocommands for "event" and file name "fname".
+///
+/// @param event event that occured
+/// @param fname filename, NULL or empty means use actual file name
+/// @param fname_io filename to use for <afile> on cmdline
+/// @param force When true, ignore autocmd_busy
+/// @param buf Buffer for <abuf>
+///
+/// @return true if some commands were executed.
+bool apply_autocmds(event_T event, char_u *fname, char_u *fname_io, bool force,
+ buf_T *buf)
{
return apply_autocmds_group(event, fname, fname_io, force,
AUGROUP_ALL, buf, NULL);
}
-/*
- * Like apply_autocmds(), but with extra "eap" argument. This takes care of
- * setting v:filearg.
- */
-static int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, exarg_T *eap)
+/// Like apply_autocmds(), but with extra "eap" argument. This takes care of
+/// setting v:filearg.
+///
+/// @param event event that occured
+/// @param fname NULL or empty means use actual file name
+/// @param fname_io fname to use for <afile> on cmdline
+/// @param force When true, ignore autocmd_busy
+/// @param buf Buffer for <abuf>
+/// @param exarg Ex command arguments
+///
+/// @return true if some commands were executed.
+static bool apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io,
+ bool force, buf_T *buf, exarg_T *eap)
{
return apply_autocmds_group(event, fname, fname_io, force,
AUGROUP_ALL, buf, eap);
}
-/*
- * Like apply_autocmds(), but handles the caller's retval. If the script
- * processing is being aborted or if retval is FAIL when inside a try
- * conditional, no autocommands are executed. If otherwise the autocommands
- * cause the script to be aborted, retval is set to FAIL.
- */
-int
-apply_autocmds_retval (
- event_T event,
- char_u *fname, /* NULL or empty means use actual file name */
- char_u *fname_io, /* fname to use for <afile> on cmdline */
- int force, /* when TRUE, ignore autocmd_busy */
- buf_T *buf, /* buffer for <abuf> */
- int *retval /* pointer to caller's retval */
-)
+/// Like apply_autocmds(), but handles the caller's retval. If the script
+/// processing is being aborted or if retval is FAIL when inside a try
+/// conditional, no autocommands are executed. If otherwise the autocommands
+/// cause the script to be aborted, retval is set to FAIL.
+///
+/// @param event event that occured
+/// @param fname NULL or empty means use actual file name
+/// @param fname_io fname to use for <afile> on cmdline
+/// @param force When true, ignore autocmd_busy
+/// @param buf Buffer for <abuf>
+/// @param[in,out] retval caller's retval
+///
+/// @return true if some autocommands were executed
+bool apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io,
+ bool force, buf_T *buf, int *retval)
{
- int did_cmd;
-
- if (should_abort(*retval))
- return FALSE;
+ if (should_abort(*retval)) {
+ return false;
+ }
- did_cmd = apply_autocmds_group(event, fname, fname_io, force,
- AUGROUP_ALL, buf, NULL);
- if (did_cmd
- && aborting()
- )
+ bool did_cmd = apply_autocmds_group(event, fname, fname_io, force,
+ AUGROUP_ALL, buf, NULL);
+ if (did_cmd && aborting()) {
*retval = FAIL;
+ }
return did_cmd;
}
-/*
- * Return TRUE when there is a CursorHold autocommand defined.
- */
-int has_cursorhold(void)
+/// Return true when there is a CursorHold/CursorHoldI autocommand defined for
+/// the current mode.
+bool has_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return first_autopat[(int)(get_real_state() == NORMAL_BUSY
? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL;
}
-/*
- * Return TRUE if the CursorHold event can be triggered.
- */
-int trigger_cursorhold(void)
+/// Return true if the CursorHold/CursorHoldI event can be triggered.
+bool trigger_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
int state;
@@ -6248,75 +6425,42 @@ int trigger_cursorhold(void)
&& !ins_compl_active()
) {
state = get_real_state();
- if (state == NORMAL_BUSY || (state & INSERT) != 0)
- return TRUE;
+ if (state == NORMAL_BUSY || (state & INSERT) != 0) {
+ return true;
+ }
}
- return FALSE;
-}
-
-/*
- * Return TRUE when there is a CursorMoved autocommand defined.
- */
-int has_cursormoved(void)
-{
- return first_autopat[(int)EVENT_CURSORMOVED] != NULL;
-}
-
-/*
- * Return TRUE when there is a CursorMovedI autocommand defined.
- */
-int has_cursormovedI(void)
-{
- return first_autopat[(int)EVENT_CURSORMOVEDI] != NULL;
-}
-
-/*
- * Return TRUE when there is a TextChanged autocommand defined.
- */
-int has_textchanged(void)
-{
- return first_autopat[(int)EVENT_TEXTCHANGED] != NULL;
+ return false;
}
-/*
- * Return TRUE when there is a TextChangedI autocommand defined.
- */
-int has_textchangedI(void)
-{
- return first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL;
-}
-
-/*
- * Return TRUE when there is an InsertCharPre autocommand defined.
- */
-int has_insertcharpre(void)
-{
- return first_autopat[(int)EVENT_INSERTCHARPRE] != NULL;
-}
-
-/// @returns true when there is an CmdUndefined autocommand defined.
-int has_cmdundefined(void)
+/// Return true if "event" autocommand is defined.
+///
+/// @param event the autocommand to check
+bool has_event(int event) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return first_autopat[(int)EVENT_CMDUNDEFINED] != NULL;
+ return first_autopat[event] != NULL;
}
-static int
-apply_autocmds_group (
- event_T event,
- char_u *fname, /* NULL or empty means use actual file name */
- char_u *fname_io, /* fname to use for <afile> on cmdline, NULL means
- use fname */
- int force, /* when TRUE, ignore autocmd_busy */
- int group, /* group ID, or AUGROUP_ALL */
- buf_T *buf, /* buffer for <abuf> */
- exarg_T *eap /* command arguments */
-)
+/// Execute autocommands for "event" and file name "fname".
+///
+/// @param event event that occured
+/// @param fname filename, NULL or empty means use actual file name
+/// @param fname_io filename to use for <afile> on cmdline,
+/// NULL means use `fname`.
+/// @param force When true, ignore autocmd_busy
+/// @param group autocmd group ID or AUGROUP_ALL
+/// @param buf Buffer for <abuf>
+/// @param eap Ex command arguments
+///
+/// @return true if some commands were executed.
+static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
+ bool force, int group, buf_T *buf,
+ exarg_T *eap)
{
char_u *sfname = NULL; /* short file name */
char_u *tail;
bool save_changed;
buf_T *old_curbuf;
- int retval = FALSE;
+ bool retval = false;
char_u *save_sourcing_name;
linenr_T save_sourcing_lnum;
char_u *save_autocmd_fname;
@@ -6470,17 +6614,16 @@ apply_autocmds_group (
}
if (fname == NULL) { /* out of memory */
xfree(sfname);
- retval = FALSE;
+ retval = false;
goto BYPASS_AU;
}
#ifdef BACKSLASH_IN_FILENAME
- /*
- * Replace all backslashes with forward slashes. This makes the
- * autocommand patterns portable between Unix and MS-DOS.
- */
- if (sfname != NULL)
+ // Replace all backslashes with forward slashes. This makes the
+ // autocommand patterns portable between Unix and Windows.
+ if (sfname != NULL) {
forward_slash(sfname);
+ }
forward_slash(fname);
#endif
@@ -6554,17 +6697,19 @@ apply_autocmds_group (
if (eap != NULL) {
save_cmdarg = set_cmdarg(eap, NULL);
set_vim_var_nr(VV_CMDBANG, (long)eap->forceit);
- } else
- save_cmdarg = NULL; /* avoid gcc warning */
- retval = TRUE;
- /* mark the last pattern, to avoid an endless loop when more patterns
- * are added when executing autocommands */
- for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next)
- ap->last = FALSE;
- ap->last = TRUE;
- check_lnums(TRUE); /* make sure cursor and topline are valid */
+ } else {
+ save_cmdarg = NULL; // avoid gcc warning
+ }
+ retval = true;
+ // mark the last pattern, to avoid an endless loop when more patterns
+ // are added when executing autocommands
+ for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next) {
+ ap->last = false;
+ }
+ ap->last = true;
+ check_lnums(true); // make sure cursor and topline are valid
do_cmdline(NULL, getnextac, (void *)&patcmd,
- DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
+ DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
if (eap != NULL) {
(void)set_cmdarg(NULL, save_cmdarg);
set_vim_var_nr(VV_CMDBANG, save_cmdbang);
@@ -6780,42 +6925,44 @@ char_u *getnextac(int c, void *cookie, int indent)
return retval;
}
-/*
- * Return TRUE if there is a matching autocommand for "fname".
- * To account for buffer-local autocommands, function needs to know
- * in which buffer the file will be opened.
- */
-int has_autocmd(event_T event, char_u *sfname, buf_T *buf)
+/// Return true if there is a matching autocommand for "fname".
+/// To account for buffer-local autocommands, function needs to know
+/// in which buffer the file will be opened.
+///
+/// @param event event that occured.
+/// @param sfname filename the event occured in.
+/// @param buf buffer the file is open in
+bool has_autocmd(event_T event, char_u *sfname, buf_T *buf)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
AutoPat *ap;
char_u *fname;
char_u *tail = path_tail(sfname);
- int retval = FALSE;
+ bool retval = false;
- fname = (char_u *)FullName_save((char *)sfname, FALSE);
- if (fname == NULL)
- return FALSE;
+ fname = (char_u *)FullName_save((char *)sfname, false);
+ if (fname == NULL) {
+ return false;
+ }
#ifdef BACKSLASH_IN_FILENAME
- /*
- * Replace all backslashes with forward slashes. This makes the
- * autocommand patterns portable between Unix and MS-DOS.
- */
+ // Replace all backslashes with forward slashes. This makes the
+ // autocommand patterns portable between Unix and Windows.
sfname = vim_strsave(sfname);
forward_slash(sfname);
forward_slash(fname);
#endif
- for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next)
+ for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) {
if (ap->pat != NULL && ap->cmds != NULL
&& (ap->buflocal_nr == 0
? match_file_pat(NULL, &ap->reg_prog, fname, sfname, tail,
ap->allow_dirs)
- : buf != NULL && ap->buflocal_nr == buf->b_fnum
- )) {
- retval = TRUE;
+ : buf != NULL && ap->buflocal_nr == buf->b_fnum)) {
+ retval = true;
break;
}
+ }
xfree(fname);
#ifdef BACKSLASH_IN_FILENAME
@@ -6903,29 +7050,27 @@ char_u *get_event_name(expand_T *xp, int idx)
}
-/*
- * Return TRUE if autocmd is supported.
- */
-int autocmd_supported(char_u *name)
+/// Return true if autocmd "event" is supported.
+bool autocmd_supported(char_u *event)
{
char_u *p;
- return event_name2nr(name, &p) != NUM_EVENTS;
+ return event_name2nr(event, &p) != NUM_EVENTS;
}
-/*
- * Return TRUE if an autocommand is defined for a group, event and
- * pattern: The group can be omitted to accept any group. "event" and "pattern"
- * can be NULL to accept any event and pattern. "pattern" can be NULL to accept
- * any pattern. Buffer-local patterns <buffer> or <buffer=N> are accepted.
- * Used for:
- * exists("#Group") or
- * exists("#Group#Event") or
- * exists("#Group#Event#pat") or
- * exists("#Event") or
- * exists("#Event#pat")
- */
-int au_exists(char_u *arg)
+/// Return true if an autocommand is defined for a group, event and
+/// pattern: The group can be omitted to accept any group.
+/// `event` and `pattern` can be omitted to accept any event and pattern.
+/// Buffer-local patterns <buffer> or <buffer=N> are accepted.
+/// Used for:
+/// exists("#Group") or
+/// exists("#Group#Event") or
+/// exists("#Group#Event#pat") or
+/// exists("#Event") or
+/// exists("#Event#pat")
+///
+/// @param arg autocommand string
+bool au_exists(const char_u *arg) FUNC_ATTR_WARN_UNUSED_RESULT
{
char_u *arg_save;
char_u *pattern = NULL;
@@ -6935,7 +7080,7 @@ int au_exists(char_u *arg)
AutoPat *ap;
buf_T *buflocal_buf = NULL;
int group;
- int retval = FALSE;
+ bool retval = false;
/* Make a copy so that we can change the '#' chars to a NUL. */
arg_save = vim_strsave(arg);
@@ -6951,8 +7096,8 @@ int au_exists(char_u *arg)
event_name = arg_save;
} else {
if (p == NULL) {
- /* "Group": group name is present and it's recognized */
- retval = TRUE;
+ // "Group": group name is present and it's recognized
+ retval = true;
goto theend;
}
@@ -6994,7 +7139,7 @@ int au_exists(char_u *arg)
|| (buflocal_buf == NULL
? fnamecmp(ap->pat, pattern) == 0
: ap->buflocal_nr == buflocal_buf->b_fnum))) {
- retval = TRUE;
+ retval = true;
break;
}
@@ -7009,12 +7154,13 @@ theend:
///
/// Used for autocommands and 'wildignore'.
///
-/// @param pattern the pattern to match with
-/// @param prog the pre-compiled regprog or NULL
-/// @param fname the full path of the file name
-/// @param sfname the short file name or NULL
-/// @param tail the tail of the path
-/// @param allow_dirs allow matching with dir
+/// @param pattern pattern to match with
+/// @param prog pre-compiled regprog or NULL
+/// @param fname full path of the file name
+/// @param sfname short file name or NULL
+/// @param tail tail of the path
+/// @param allow_dirs Allow matching with dir
+///
/// @return true if there is a match, false otherwise
static bool match_file_pat(char_u *pattern, regprog_T **prog, char_u *fname,
char_u *sfname, char_u *tail, int allow_dirs)
@@ -7053,12 +7199,17 @@ static bool match_file_pat(char_u *pattern, regprog_T **prog, char_u *fname,
return result;
}
-/*
- * Return TRUE if a file matches with a pattern in "list".
- * "list" is a comma-separated list of patterns, like 'wildignore'.
- * "sfname" is the short file name or NULL, "ffname" the long file name.
- */
-int match_file_list(char_u *list, char_u *sfname, char_u *ffname)
+/// Check if a file matches with a pattern in "list".
+/// "list" is a comma-separated list of patterns, like 'wildignore'.
+/// "sfname" is the short file name or NULL, "ffname" the long file name.
+///
+/// @param list list of patterns to match
+/// @param sfname short file name
+/// @param ffname full file name
+///
+/// @return true if there was a match
+bool match_file_list(char_u *list, char_u *sfname, char_u *ffname)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 3)
{
char_u buf[100];
char_u *tail;
@@ -7069,42 +7220,41 @@ int match_file_list(char_u *list, char_u *sfname, char_u *ffname)
tail = path_tail(sfname);
- /* try all patterns in 'wildignore' */
+ // try all patterns in 'wildignore'
p = list;
while (*p) {
- copy_option_part(&p, buf, 100, ",");
- regpat = file_pat_to_reg_pat(buf, NULL, &allow_dirs, FALSE);
- if (regpat == NULL)
+ copy_option_part(&p, buf, ARRAY_SIZE(buf), ",");
+ regpat = file_pat_to_reg_pat(buf, NULL, &allow_dirs, false);
+ if (regpat == NULL) {
break;
- match = match_file_pat(regpat, NULL, ffname, sfname,
- tail, (int)allow_dirs);
+ }
+ match = match_file_pat(regpat, NULL, ffname, sfname, tail, (int)allow_dirs);
xfree(regpat);
- if (match)
- return TRUE;
+ if (match) {
+ return true;
+ }
}
- return FALSE;
+ return false;
}
-/*
- * Convert the given pattern "pat" which has shell style wildcards in it, into
- * a regular expression, and return the result in allocated memory. If there
- * is a directory path separator to be matched, then TRUE is put in
- * allow_dirs, otherwise FALSE is put there -- webb.
- * Handle backslashes before special characters, like "\*" and "\ ".
- *
- * Returns NULL on failure.
- */
-char_u *
-file_pat_to_reg_pat (
- char_u *pat,
- char_u *pat_end, /* first char after pattern or NULL */
- char *allow_dirs, /* Result passed back out in here */
- int no_bslash /* Don't use a backward slash as pathsep */
+/// Convert the given pattern "pat" which has shell style wildcards in it, into
+/// a regular expression, and return the result in allocated memory. If there
+/// is a directory path separator to be matched, then TRUE is put in
+/// allow_dirs, otherwise FALSE is put there -- webb.
+/// Handle backslashes before special characters, like "\*" and "\ ".
+///
+/// Returns NULL on failure.
+char_u * file_pat_to_reg_pat(
+ const char_u *pat,
+ const char_u *pat_end, // first char after pattern or NULL
+ char *allow_dirs, // Result passed back out in here
+ int no_bslash // Don't use a backward slash as pathsep
)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- char_u *endp;
+ const char_u *endp;
char_u *reg_pat;
- char_u *p;
+ const char_u *p;
int nested = 0;
int add_dollar = TRUE;
@@ -7113,6 +7263,10 @@ file_pat_to_reg_pat (
if (pat_end == NULL)
pat_end = pat + STRLEN(pat);
+ if (pat_end == pat) {
+ return (char_u *)xstrdup("^$");
+ }
+
size_t size = 2; // '^' at start, '$' at end.
for (p = pat; p < pat_end; p++) {
@@ -7150,10 +7304,11 @@ 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) {
@@ -7208,12 +7363,12 @@ 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 2e32e78062..ac3cf959c8 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -762,14 +762,18 @@ 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;
}
- /* Mark all folds from top to bot as maybe-small. */
- (void)foldFind(&curwin->w_folds, top, &fp);
- while (fp < (fold_T *)curwin->w_folds.ga_data + curwin->w_folds.ga_len
+ // Mark all folds from top to bot as maybe-small.
+ (void)foldFind(&wp->w_folds, top, &fp);
+ while (fp < (fold_T *)wp->w_folds.ga_data + wp->w_folds.ga_len
&& fp->fd_top < bot) {
fp->fd_small = MAYBE;
++fp;
@@ -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/garray.c b/src/nvim/garray.c
index 75c3fb9a73..98cec69b54 100644
--- a/src/nvim/garray.c
+++ b/src/nvim/garray.c
@@ -174,6 +174,7 @@ char_u* ga_concat_strings(const garray_T *gap) FUNC_ATTR_NONNULL_RET
}
/// Concatenate a string to a growarray which contains characters.
+/// When "s" is NULL does not do anything.
///
/// WARNING:
/// - Does NOT copy the NUL at the end!
@@ -183,12 +184,27 @@ char_u* ga_concat_strings(const garray_T *gap) FUNC_ATTR_NONNULL_RET
/// @param s
void ga_concat(garray_T *gap, const char_u *restrict s)
{
- int len = (int)strlen((char *) s);
+ if (s == NULL) {
+ return;
+ }
+
+ 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 44d6c086e7..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
}
/*
@@ -2940,8 +2949,12 @@ do_map (
if (!did_it) {
retval = 2; /* no match */
} else if (*keys == Ctrl_C) {
- /* If CTRL-C has been unmapped, reuse it for Interrupting. */
- mapped_ctrl_c = FALSE;
+ // If CTRL-C has been unmapped, reuse it for Interrupting.
+ if (map_table == curbuf->b_maphash) {
+ curbuf->b_mapped_ctrl_c &= ~mode;
+ } else {
+ mapped_ctrl_c &= ~mode;
+ }
}
goto theend;
}
@@ -2966,9 +2979,14 @@ do_map (
*/
mp = xmalloc(sizeof(mapblock_T));
- /* If CTRL-C has been mapped, don't always use it for Interrupting. */
- if (*keys == Ctrl_C)
- mapped_ctrl_c = TRUE;
+ // If CTRL-C has been mapped, don't always use it for Interrupting.
+ if (*keys == Ctrl_C) {
+ if (map_table == curbuf->b_maphash) {
+ curbuf->b_mapped_ctrl_c |= mode;
+ } else {
+ mapped_ctrl_c |= mode;
+ }
+ }
mp->m_keys = vim_strsave(keys);
mp->m_str = vim_strsave(rhs);
@@ -3001,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;
@@ -3069,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;
@@ -3121,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;
}
/*
@@ -3259,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;
@@ -3454,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;
@@ -3472,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) */
@@ -3636,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 */
@@ -3646,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 */
@@ -4179,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 52eebebf41..49d1de21d9 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -2,19 +2,9 @@
#define NVIM_GLOBALS_H
#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(x)
-#else
-# ifndef INIT
-# define INIT(x) x
-# define DO_INIT
-# 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 */
@@ -116,12 +112,9 @@
#define DFLT_COLS 80 /* default value for 'columns' */
#define DFLT_ROWS 24 /* default value for 'lines' */
-EXTERN long Rows /* nr of rows in the screen */
-#ifdef DO_INIT
- = DFLT_ROWS
-#endif
-;
-EXTERN long Columns INIT(= DFLT_COLS); /* nr of columns in the screen */
+EXTERN long Rows INIT(= DFLT_ROWS); // nr of rows in the screen
+
+EXTERN long Columns INIT(= DFLT_COLS); // nr of columns in the screen
/*
* The characters and attributes cached for the screen.
@@ -162,15 +155,6 @@ EXTERN int Screen_mco INIT(= 0); /* value of p_mco used when
* These are single-width. */
EXTERN schar_T *ScreenLines2 INIT(= NULL);
-/*
- * Indexes for tab page line:
- * N > 0 for label of tab page N
- * N == 0 for no label
- * N < 0 for closing tab page -N
- * N == -999 for closing current tab page
- */
-EXTERN short *TabPageIdxs INIT(= NULL);
-
EXTERN int screen_Rows INIT(= 0); /* actual size of ScreenLines[] */
EXTERN int screen_Columns INIT(= 0); /* actual size of ScreenLines[] */
@@ -220,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 */
@@ -750,11 +738,8 @@ EXTERN int can_si INIT(= FALSE);
*/
EXTERN int can_si_back INIT(= FALSE);
-EXTERN pos_T saved_cursor /* w_cursor before formatting text. */
-#ifdef DO_INIT
- = INIT_POS_T(0, 0, 0)
-#endif
-;
+// w_cursor before formatting text.
+EXTERN pos_T saved_cursor INIT(= INIT_POS_T(0, 0, 0));
/*
* Stuff for insert mode.
@@ -797,6 +782,8 @@ EXTERN bool enc_utf8 INIT(= false); /* UTF-8 encoded Unicode */
EXTERN int enc_latin1like INIT(= TRUE); /* 'encoding' is latin1 comp. */
EXTERN int has_mbyte INIT(= 0); /* any multi-byte encoding */
+/// Encoding used when 'fencs' is set to "default"
+EXTERN char_u *fenc_default INIT(= NULL);
/*
* To speed up BYTELEN() we fill a table with the byte lengths whenever
@@ -884,7 +871,7 @@ EXTERN int ctrl_x_mode INIT(= 0); /* Which Ctrl-X mode are we in? */
EXTERN int no_abbr INIT(= TRUE); /* TRUE when no abbreviations loaded */
-EXTERN int mapped_ctrl_c INIT(= FALSE); /* CTRL-C is mapped */
+EXTERN int mapped_ctrl_c INIT(= 0); // Modes where CTRL-C is mapped.
EXTERN cmdmod_T cmdmod; /* Ex command modifiers */
@@ -914,22 +901,19 @@ EXTERN int RedrawingDisabled INIT(= 0);
EXTERN int readonlymode INIT(= FALSE); /* Set to TRUE for "view" */
EXTERN int recoverymode INIT(= FALSE); /* Set to TRUE for "-r" option */
-EXTERN typebuf_T typebuf /* typeahead buffer */
-#ifdef DO_INIT
- = {NULL, NULL, 0, 0, 0, 0, 0, 0, 0}
-#endif
-;
-EXTERN int ex_normal_busy INIT(= 0); /* recursiveness of ex_normal() */
-EXTERN int ex_normal_lock INIT(= 0); /* forbid use of ex_normal() */
-EXTERN int ignore_script INIT(= FALSE); /* ignore script input */
-EXTERN int stop_insert_mode; /* for ":stopinsert" and 'insertmode' */
+// typeahead buffer
+EXTERN typebuf_T typebuf INIT(= { NULL, NULL, 0, 0, 0, 0, 0, 0, 0 });
-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 int ex_normal_busy INIT(= 0); // recursiveness of ex_normal()
+EXTERN int ex_normal_lock INIT(= 0); // forbid use of ex_normal()
+EXTERN int ignore_script INIT(= false); // ignore script input
+EXTERN int stop_insert_mode; // for ":stopinsert" and 'insertmode'
+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 */
@@ -956,30 +940,28 @@ EXTERN reg_extmatch_T *re_extmatch_in INIT(= NULL); /* Used by vim_regexec():
EXTERN reg_extmatch_T *re_extmatch_out INIT(= NULL); /* Set by vim_regexec()
* to store \z\(...\) matches */
-EXTERN int did_outofmem_msg INIT(= FALSE);
-/* set after out of memory msg */
-EXTERN int did_swapwrite_msg INIT(= FALSE);
-/* set after swap write error msg */
-EXTERN int undo_off INIT(= FALSE); /* undo switched off for now */
-EXTERN int global_busy INIT(= 0); /* set when :global is executing */
-EXTERN int listcmd_busy INIT(= FALSE); /* set when :argdo, :windo or
- :bufdo is executing */
-EXTERN int need_start_insertmode INIT(= FALSE);
-/* start insert mode soon */
-EXTERN char_u *last_cmdline INIT(= NULL); /* last command line (for ":) */
-EXTERN char_u *repeat_cmdline INIT(= NULL); /* command line for "." */
-EXTERN char_u *new_last_cmdline INIT(= NULL); /* new value for last_cmdline */
-EXTERN char_u *autocmd_fname INIT(= NULL); /* fname for <afile> on cmdline */
-EXTERN int autocmd_fname_full; /* autocmd_fname is full path */
-EXTERN int autocmd_bufnr INIT(= 0); /* fnum for <abuf> on cmdline */
-EXTERN char_u *autocmd_match INIT(= NULL); /* name for <amatch> on cmdline */
-EXTERN int did_cursorhold INIT(= FALSE); /* set when CursorHold t'gerd */
-EXTERN pos_T last_cursormoved /* for CursorMoved event */
-# ifdef DO_INIT
- = INIT_POS_T(0, 0, 0)
-# endif
-;
-EXTERN int last_changedtick INIT(= 0); /* for TextChanged event */
+EXTERN int did_outofmem_msg INIT(= false);
+// set after out of memory msg
+EXTERN int did_swapwrite_msg INIT(= false);
+// set after swap write error msg
+EXTERN int undo_off INIT(= false); // undo switched off for now
+EXTERN int global_busy INIT(= 0); // set when :global is executing
+EXTERN int listcmd_busy INIT(= false); // set when :argdo, :windo or
+ // :bufdo is executing
+EXTERN int need_start_insertmode INIT(= false);
+// start insert mode soon
+EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":)
+EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "."
+EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline
+EXTERN char_u *autocmd_fname INIT(= NULL); // fname for <afile> on cmdline
+EXTERN int autocmd_fname_full; // autocmd_fname is full path
+EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline
+EXTERN char_u *autocmd_match INIT(= NULL); // name for <amatch> on cmdline
+EXTERN int did_cursorhold INIT(= false); // set when CursorHold t'gerd
+// for CursorMoved event
+EXTERN pos_T last_cursormoved INIT(= INIT_POS_T(0, 0, 0));
+
+EXTERN int last_changedtick INIT(= 0); // for TextChanged event
EXTERN buf_T *last_changedtick_buf INIT(= NULL);
EXTERN int postponed_split INIT(= 0); /* for CTRL-W CTRL-] command */
@@ -1089,13 +1071,9 @@ EXTERN int typebuf_was_filled INIT(= FALSE); /* received text from client
#ifdef BACKSLASH_IN_FILENAME
-EXTERN char psepc INIT(= '\\'); /* normal path separator character */
-EXTERN char psepcN INIT(= '/'); /* abnormal path separator character */
-EXTERN char pseps[2] /* normal path separator string */
-# ifdef DO_INIT
- = {'\\', 0}
-# endif
-;
+EXTERN char psepc INIT(= '\\'); // normal path separator character
+EXTERN char psepcN INIT(= '/'); // abnormal path separator character
+EXTERN char pseps[2] INIT(= { '\\', 0 }); // normal path separator string
#endif
/* Set to TRUE when an operator is being executed with virtual editing, MAYBE
@@ -1114,12 +1092,8 @@ EXTERN int need_cursor_line_redraw INIT(= FALSE);
#ifdef USE_MCH_ERRMSG
-/* Grow array to collect error messages in until they can be displayed. */
-EXTERN garray_T error_ga
-# ifdef DO_INIT
- = GA_EMPTY_INIT_VALUE
-# endif
-;
+// Grow array to collect error messages in until they can be displayed.
+EXTERN garray_T error_ga INIT(= GA_EMPTY_INIT_VALUE);
#endif
@@ -1239,6 +1213,7 @@ EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s"));
EXTERN char_u e_maxmempat[] INIT(= N_(
"E363: pattern uses more memory than 'maxmempattern'"));
EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer"));
+EXTERN char_u e_nobufnr[] INIT(= N_("E86: Buffer %" PRId64 " does not exist"));
EXTERN char_u e_invalpat[] INIT(= N_(
"E682: Invalid search pattern or delimiter"));
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 3585c26ca2..2f9ec0b3ff 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -11,6 +11,7 @@
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
+#include <fcntl.h>
#include "nvim/vim.h"
#include "nvim/ascii.h"
@@ -26,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"
@@ -743,8 +743,16 @@ err_closing:
(void)close(to_cs[1]);
(void)close(from_cs[0]);
#else
- /* WIN32 */
- /* Create pipes to communicate with cscope */
+ // Create pipes to communicate with cscope
+ int fd;
+ SECURITY_ATTRIBUTES sa;
+ PROCESS_INFORMATION pi;
+ BOOL pipe_stdin = FALSE, pipe_stdout = FALSE; // NOLINT(readability/bool)
+ STARTUPINFO si;
+ HANDLE stdin_rd, stdout_rd;
+ HANDLE stdout_wr, stdin_wr;
+ BOOL created;
+
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
@@ -861,17 +869,16 @@ err_closing:
csinfo[i].hProc = pi.hProcess;
CloseHandle(pi.hThread);
- /* TODO - tidy up after failure to create files on pipe handles. */
- if (((fd = _open_osfhandle((OPEN_OH_ARGTYPE)stdin_wr,
- _O_TEXT|_O_APPEND)) < 0)
- || ((csinfo[i].to_fp = _fdopen(fd, "w")) == NULL))
+ // TODO(neovim): tidy up after failure to create files on pipe handles.
+ if (((fd = _open_osfhandle((intptr_t)stdin_wr, _O_TEXT|_O_APPEND)) < 0)
+ || ((csinfo[i].to_fp = _fdopen(fd, "w")) == NULL)) {
PERROR(_("cs_create_connection: fdopen for to_fp failed"));
- if (((fd = _open_osfhandle((OPEN_OH_ARGTYPE)stdout_rd,
- _O_TEXT|_O_RDONLY)) < 0)
- || ((csinfo[i].fr_fp = _fdopen(fd, "r")) == NULL))
+ }
+ if (((fd = _open_osfhandle((intptr_t)stdout_rd, _O_TEXT|_O_RDONLY)) < 0)
+ || ((csinfo[i].fr_fp = _fdopen(fd, "r")) == NULL)) {
PERROR(_("cs_create_connection: fdopen for fr_fp failed"));
-
- /* Close handles for file descriptors inherited by the cscope process */
+ }
+ // Close handles for file descriptors inherited by the cscope process.
CloseHandle(stdin_rd);
CloseHandle(stdout_wr);
@@ -1055,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;
}
@@ -1146,22 +1153,6 @@ static void clear_csinfo(size_t i)
csinfo[i].to_fp = NULL;
}
-#ifndef UNIX
-static char *GetWin32Error(void)
-{
- char *msg = NULL;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
- NULL, GetLastError(), 0, (LPSTR)&msg, 0, NULL);
- if (msg != NULL) {
- /* remove trailing \r\n */
- char *pcrlf = strstr(msg, "\r\n");
- if (pcrlf != NULL)
- *pcrlf = '\0';
- }
- return msg;
-}
-#endif
-
/*
* PRIVATE: cs_insert_filelist
*
@@ -1640,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:
@@ -1721,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');
@@ -1734,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
@@ -1850,9 +1845,7 @@ static void sig_handler(int s) {
*/
static void cs_release_csp(size_t i, int freefnpp)
{
- /*
- * Trying to exit normally (not sure whether it is fit to UNIX cscope
- */
+ // Trying to exit normally (not sure whether it is fit to Unix cscope)
if (csinfo[i].to_fp != NULL) {
(void)fputs("q\n", csinfo[i].to_fp);
(void)fflush(csinfo[i].to_fp);
@@ -2087,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/if_cscope_defs.h b/src/nvim/if_cscope_defs.h
index a0c84e8ba1..8cd74c74e6 100644
--- a/src/nvim/if_cscope_defs.h
+++ b/src/nvim/if_cscope_defs.h
@@ -16,6 +16,7 @@
# include <sys/types.h> /* pid_t */
#endif
+#include "nvim/os/os_defs.h"
#include "nvim/os/fs_defs.h"
#define CSCOPE_SUCCESS 0
@@ -50,7 +51,13 @@ typedef struct csi {
char * ppath; /* path to prepend (the -P option) */
char * flags; /* additional cscope flags/options (e.g, -p2) */
#if defined(UNIX)
- pid_t pid; /* PID of the connected cscope process. */
+ pid_t pid; // PID of the connected cscope process
+#else
+ DWORD pid; // PID of the connected cscope process
+ HANDLE hProc; // cscope process handle
+ DWORD nVolume; // Volume serial number, instead of st_dev
+ DWORD nIndexHigh; // st_ino has no meaning on Windows
+ DWORD nIndexLow;
#endif
FileID file_id;
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 d62e7aad03..efe8e73a3c 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -17,7 +17,11 @@
#include "nvim/search.h"
#include "nvim/strings.h"
-
+// Find result cache for cpp_baseclass
+typedef struct {
+ int found;
+ lpos_T lpos;
+} cpp_baseclass_cache_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "indent_c.c.generated.h"
@@ -25,6 +29,7 @@
/*
* Find the start of a comment, not knowing if we are in a comment right now.
* Search starts at w_cursor.lnum and goes backwards.
+ * Return NULL when not inside a comment.
*/
static pos_T *ind_find_start_comment(void)
{ /* XXX */
@@ -64,6 +69,72 @@ 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.
+///
+/// @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
+ 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;
+}
+
+/*
+ * Find the start of a raw string, not knowing if we are in one right now.
+ * Search starts at w_cursor.lnum and goes backwards.
+ * Return NULL when not inside a raw string.
+ */
+static pos_T *find_start_rawstring(int ind_maxcomment)
+{ /* XXX */
+ pos_T *pos;
+ char_u *line;
+ char_u *p;
+ long cur_maxcomment = ind_maxcomment;
+
+ for (;;)
+ {
+ pos = findmatchlimit(NULL, 'R', FM_BACKWARD, cur_maxcomment);
+ if (pos == NULL)
+ break;
+
+ /*
+ * Check if the raw string start we found is inside a string.
+ * If it is then restrict the search to below this line and try again.
+ */
+ line = ml_get(pos->lnum);
+ for (p = line; *p && (colnr_T)(p - line) < pos->col; ++p)
+ p = skip_string(p);
+ if ((colnr_T)(p - line) <= pos->col)
+ break;
+ cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1;
+ if (cur_maxcomment <= 0)
+ {
+ pos = NULL;
+ break;
+ }
+ }
+ return pos;
+}
+
/*
* Skip to the end of a "string" and a 'c' character.
* If there is no string or character, return argument unmodified.
@@ -97,7 +168,26 @@ static char_u *skip_string(char_u *p)
break;
}
if (p[0] == '"')
- continue;
+ continue; /* continue for another string */
+ } else if (p[0] == 'R' && p[1] == '"') {
+ /* Raw string: R"[delim](...)[delim]" */
+ char_u *delim = p + 2;
+ char_u *paren = vim_strchr(delim, '(');
+
+ if (paren != NULL)
+ {
+ long delim_len = paren - delim;
+
+ for (p += 3; *p; ++p)
+ if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0
+ && p[delim_len + 1] == '"')
+ {
+ p += delim_len + 1;
+ break;
+ }
+ if (p[0] == '"')
+ continue; /* continue for another string */
+ }
}
break; /* no string found */
}
@@ -288,10 +378,11 @@ int cin_islabel(void)
--curwin->w_cursor.lnum;
/*
- * If we're in a comment now, skip to the start of the comment.
+ * If we're in a comment or raw string now, skip to the start of
+ * it.
*/
curwin->w_cursor.col = 0;
- if ((trypos = ind_find_start_comment()) != NULL) /* XXX */
+ if ((trypos = ind_find_start_CORS()) != NULL) /* XXX */
curwin->w_cursor = *trypos;
line = get_cursor_line_ptr();
@@ -766,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)) {
@@ -986,17 +1091,18 @@ static int cin_isbreak(char_u *p)
*
* This is a lot of guessing. Watch out for "cond ? func() : foo".
*/
-static int
-cin_is_cpp_baseclass (
- colnr_T *col /* return: column to align with */
-)
-{
+static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) {
+ lpos_T *pos = &cached->lpos; // find position
char_u *s;
int class_or_struct, lookfor_ctor_init, cpp_base_class;
linenr_T lnum = curwin->w_cursor.lnum;
char_u *line = get_cursor_line_ptr();
- *col = 0;
+ if (pos->lnum <= lnum) {
+ return cached->found; // Use the cached result
+ }
+
+ pos->col = 0;
s = skipwhite(line);
if (*s == '#') /* skip #define FOO x ? (x) : x */
@@ -1038,20 +1144,29 @@ cin_is_cpp_baseclass (
--lnum;
}
+ 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;
}
- if (s[0] == '"')
+ if (s[0] == '"' || (s[0] == 'R' && s[1] == '"'))
s = skip_string(s) + 1;
else if (s[0] == ':') {
if (s[1] == ':') {
@@ -1062,9 +1177,9 @@ cin_is_cpp_baseclass (
} else if (lookfor_ctor_init || class_or_struct) {
/* we have something found, that looks like the start of
* cpp-base-class-declaration or constructor-initialization */
- cpp_base_class = TRUE;
- lookfor_ctor_init = class_or_struct = FALSE;
- *col = 0;
+ cpp_base_class = true;
+ lookfor_ctor_init = class_or_struct = false;
+ pos->col = 0;
s = cin_skipcomment(s + 1);
} else
s = cin_skipcomment(s + 1);
@@ -1090,25 +1205,31 @@ cin_is_cpp_baseclass (
return FALSE;
} else if (!vim_isIDc(s[0])) {
/* if it is not an identifier, we are wrong */
- class_or_struct = FALSE;
- lookfor_ctor_init = FALSE;
- } else if (*col == 0) {
+ class_or_struct = false;
+ lookfor_ctor_init = false;
+ } else if (pos->col == 0) {
/* it can't be a constructor-initialization any more */
lookfor_ctor_init = FALSE;
/* the first statement starts here: lineup with this one... */
- if (cpp_base_class)
- *col = (colnr_T)(s - line);
+ if (cpp_base_class) {
+ pos->col = (colnr_T)(s - line);
+ }
}
/* When the line ends in a comma don't align with it. */
- if (lnum == curwin->w_cursor.lnum && *s == ',' && cin_nocode(s + 1))
- *col = 0;
+ if (lnum == curwin->w_cursor.lnum && *s == ',' && cin_nocode(s + 1)) {
+ pos->col = 0;
+ }
s = cin_skipcomment(s + 1);
}
}
+ cached->found = cpp_base_class;
+ if (cpp_base_class) {
+ pos->lnum = lnum;
+ }
return cpp_base_class;
}
@@ -1216,7 +1337,7 @@ static pos_T *find_start_brace(void)
pos = NULL;
/* ignore the { if it's in a // or / * * / comment */
if ((colnr_T)cin_skip2pos(trypos) == trypos->col
- && (pos = ind_find_start_comment()) == NULL) /* XXX */
+ && (pos = ind_find_start_CORS()) == NULL) /* XXX */
break;
if (pos != NULL)
curwin->w_cursor.lnum = pos->lnum;
@@ -1237,18 +1358,36 @@ static pos_T * find_match_char(char_u c, int ind_maxparen)
pos_T cursor_save;
pos_T *trypos;
static pos_T pos_copy;
+ int ind_maxp_wk;
cursor_save = curwin->w_cursor;
- if ((trypos = findmatchlimit(NULL, c, 0, ind_maxparen)) != NULL) {
- /* check if the ( is in a // comment */
- if ((colnr_T)cin_skip2pos(trypos) > trypos->col)
+ ind_maxp_wk = ind_maxparen;
+retry:
+ if ((trypos = findmatchlimit(NULL, c, 0, ind_maxp_wk)) != NULL) {
+ // check if the ( is in a // comment
+ if ((colnr_T)cin_skip2pos(trypos) > trypos->col) {
+ ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum - trypos->lnum);
+ if (ind_maxp_wk > 0) {
+ curwin->w_cursor = *trypos;
+ curwin->w_cursor.col = 0; // XXX
+ goto retry;
+ }
trypos = NULL;
- else {
+ } else {
+ pos_T *trypos_wk;
+
pos_copy = *trypos; /* copy trypos, findmatch will change it */
trypos = &pos_copy;
curwin->w_cursor = *trypos;
- if (ind_find_start_comment() != NULL)
+ if ((trypos_wk = ind_find_start_CORS()) != NULL) { /* XXX */
+ ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum
+ - trypos_wk->lnum);
+ if (ind_maxp_wk > 0) {
+ curwin->w_cursor = *trypos_wk;
+ goto retry;
+ }
trypos = NULL;
+ }
}
}
curwin->w_cursor = cursor_save;
@@ -1532,6 +1671,10 @@ void parse_cino(buf_T *buf)
}
}
+/*
+ * Return the desired indent for C code.
+ * Return -1 if the indent should be left alone (inside a raw string).
+ */
int get_c_indent(void)
{
pos_T cur_curpos;
@@ -1542,8 +1685,9 @@ int get_c_indent(void)
char_u *theline;
char_u *linecopy;
pos_T *trypos;
+ pos_T *comment_pos;
pos_T *tryposBrace = NULL;
- pos_T tryposBraceCopy;
+ pos_T tryposCopy;
pos_T our_paren_pos;
char_u *start;
int start_brace;
@@ -1567,7 +1711,7 @@ int get_c_indent(void)
#define LOOKFOR_CPP_BASECLASS 9
#define LOOKFOR_ENUM_OR_INIT 10
#define LOOKFOR_JS_KEY 11
-#define LOOKFOR_NO_COMMA 12
+#define LOOKFOR_COMMA 12
int whilelevel;
linenr_T lnum;
@@ -1578,6 +1722,7 @@ int get_c_indent(void)
int cont_amount = 0; /* amount for continuation line */
int original_line_islabel;
int added_to_amount = 0;
+ cpp_baseclass_cache_T cache_cpp_baseclass = { false, { MAXLNUM, 0 } };
/* make a copy, value is changed below */
int ind_continuation = curbuf->b_ind_continuation;
@@ -1585,7 +1730,7 @@ int get_c_indent(void)
/* remember where the cursor was when we started */
cur_curpos = curwin->w_cursor;
- /* if we are at line 1 0 is fine, right? */
+ /* if we are at line 1 zero indent is fine, right? */
if (cur_curpos.lnum == 1)
return 0;
@@ -1615,37 +1760,55 @@ int get_c_indent(void)
original_line_islabel = cin_islabel(); /* XXX */
/*
+ * If we are inside a raw string don't change the indent.
+ * Ignore a raw string inside a comment.
+ */
+ comment_pos = ind_find_start_comment();
+ if (comment_pos != NULL) {
+ /* findmatchlimit() static pos is overwritten, make a copy */
+ tryposCopy = *comment_pos;
+ comment_pos = &tryposCopy;
+ }
+ trypos = find_start_rawstring(curbuf->b_ind_maxcomment);
+ if (trypos != NULL && (comment_pos == NULL || lt(*trypos, *comment_pos))) {
+ amount = -1;
+ goto laterend;
+ }
+
+ /*
* #defines and so on always go at the left when included in 'cinkeys'.
*/
- if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', TRUE)))
+ if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', true))) {
amount = curbuf->b_ind_hash_comment;
+ goto theend;
+ }
/*
* Is it a non-case label? Then that goes at the left margin too unless:
* - JS flag is set.
* - 'L' item has a positive value.
*/
- else if (original_line_islabel && !curbuf->b_ind_js
+ if (original_line_islabel && !curbuf->b_ind_js
&& curbuf->b_ind_jump_label < 0) {
amount = 0;
+ goto theend;
}
/*
* If we're inside a "//" comment and there is a "//" comment in a
* previous line, lineup with that one.
*/
- else if (cin_islinecomment(theline)
+ if (cin_islinecomment(theline)
&& (trypos = find_line_comment()) != NULL) { /* XXX */
/* find how indented the line beginning the comment is */
getvcol(curwin, trypos, &col, NULL, NULL);
amount = col;
+ goto theend;
}
/*
* If we're inside a comment and not looking at the start of the
* comment, try using the 'comments' option.
*/
- else if (!cin_iscomment(theline)
- && (trypos = ind_find_start_comment()) != NULL) {
- /* XXX */
+ if (!cin_iscomment(theline) && comment_pos != NULL) { /* XXX */
int lead_start_len = 2;
int lead_middle_len = 1;
char_u lead_start[COM_MAX_LEN]; /* start-comment string */
@@ -1657,7 +1820,7 @@ int get_c_indent(void)
int done = FALSE;
/* find how indented the line beginning the comment is */
- getvcol(curwin, trypos, &col, NULL, NULL);
+ getvcol(curwin, comment_pos, &col, NULL, NULL);
amount = col;
*lead_start = NUL;
*lead_middle = NUL;
@@ -1709,12 +1872,12 @@ int get_c_indent(void)
lead_middle_len) == 0) {
amount = get_indent_lnum(curwin->w_cursor.lnum - 1);
break;
- }
- /* If the start comment string doesn't match with the
- * start of the comment, skip this entry. XXX */
- else if (STRNCMP(ml_get(trypos->lnum) + trypos->col,
- lead_start, lead_start_len) != 0)
+ } else if (STRNCMP(ml_get(comment_pos->lnum) + comment_pos->col,
+ lead_start, lead_start_len) != 0) {
+ /* If the start comment string doesn't match with the
+ * start of the comment, skip this entry. XXX */
continue;
+ }
}
if (start_off != 0)
amount += start_off;
@@ -1758,7 +1921,7 @@ int get_c_indent(void)
* otherwise, add the amount specified by "c" in 'cino'
*/
amount = -1;
- for (lnum = cur_curpos.lnum - 1; lnum > trypos->lnum; --lnum) {
+ for (lnum = cur_curpos.lnum - 1; lnum > comment_pos->lnum; --lnum) {
if (linewhite(lnum)) /* skip blank lines */
continue;
amount = get_indent_lnum(lnum); /* XXX */
@@ -1766,28 +1929,30 @@ int get_c_indent(void)
}
if (amount == -1) { /* use the comment opener */
if (!curbuf->b_ind_in_comment2) {
- start = ml_get(trypos->lnum);
- look = start + trypos->col + 2; /* skip / and * */
+ start = ml_get(comment_pos->lnum);
+ look = start + comment_pos->col + 2; /* skip / and * */
if (*look != NUL) /* if something after it */
- trypos->col = (colnr_T)(skipwhite(look) - start);
+ comment_pos->col = (colnr_T)(skipwhite(look) - start);
}
- getvcol(curwin, trypos, &col, NULL, NULL);
+ getvcol(curwin, comment_pos, &col, NULL, NULL);
amount = col;
if (curbuf->b_ind_in_comment2 || *look == NUL)
amount += curbuf->b_ind_in_comment;
}
}
+ goto theend;
}
// Are we looking at a ']' that has a match?
- else if (*skipwhite(theline) == ']'
+ if (*skipwhite(theline) == ']'
&& (trypos = find_match_char('[', curbuf->b_ind_maxparen)) != NULL) {
// align with the line containing the '['.
amount = get_indent_lnum(trypos->lnum);
+ goto theend;
}
/*
* Are we inside parentheses or braces?
*/ /* XXX */
- else if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL
+ if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL
&& curbuf->b_ind_java == 0)
|| (tryposBrace = find_start_brace()) != NULL
|| trypos != NULL) {
@@ -1821,8 +1986,8 @@ int get_c_indent(void)
continue; /* ignore #define, #if, etc. */
curwin->w_cursor.lnum = lnum;
- /* Skip a comment. XXX */
- if ((trypos = ind_find_start_comment()) != NULL) {
+ /* Skip a comment or raw string. XXX */
+ if ((trypos = ind_find_start_CORS()) != NULL) {
lnum = trypos->lnum + 1;
continue;
}
@@ -2023,8 +2188,8 @@ int get_c_indent(void)
// stored in tryposBrace.
// Make a copy of tryposBrace, it may point to pos_copy inside
// find_start_brace(), which may be changed somewhere.
- tryposBraceCopy = *tryposBrace;
- tryposBrace = &tryposBraceCopy;
+ tryposCopy = *tryposBrace;
+ tryposBrace = &tryposCopy;
trypos = tryposBrace;
ourscope = trypos->lnum;
start = ml_get(ourscope);
@@ -2117,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());
@@ -2201,10 +2365,10 @@ int get_c_indent(void)
l = get_cursor_line_ptr();
/*
- * If we're in a comment now, skip to the start of the
- * comment.
+ * If we're in a comment or raw string now, skip to
+ * the start of it.
*/
- trypos = ind_find_start_comment();
+ trypos = ind_find_start_CORS();
if (trypos != NULL) {
curwin->w_cursor.lnum = trypos->lnum + 1;
curwin->w_cursor.col = 0;
@@ -2286,7 +2450,8 @@ int get_c_indent(void)
amount += ind_continuation;
} else {
if (lookfor != LOOKFOR_TERM
- && lookfor != LOOKFOR_CPP_BASECLASS) {
+ && lookfor != LOOKFOR_CPP_BASECLASS
+ && lookfor != LOOKFOR_COMMA) {
amount = scope_amount;
if (theline[0] == '{') {
amount += curbuf->b_ind_open_extra;
@@ -2309,9 +2474,9 @@ int get_c_indent(void)
l = get_cursor_line_ptr();
- /* If we're in a comment now, skip to the start of
- * the comment. */
- trypos = ind_find_start_comment();
+ /* If we're in a comment or raw string now, skip
+ * to the start of it. */
+ trypos = ind_find_start_CORS();
if (trypos != NULL) {
curwin->w_cursor.lnum = trypos->lnum + 1;
curwin->w_cursor.col = 0;
@@ -2337,9 +2502,10 @@ int get_c_indent(void)
}
/*
- * If we're in a comment now, skip to the start of the comment.
+ * If we're in a comment or raw string now, skip to the start
+ * of it.
*/ /* XXX */
- if ((trypos = ind_find_start_comment()) != NULL) {
+ if ((trypos = ind_find_start_CORS()) != NULL) {
curwin->w_cursor.lnum = trypos->lnum + 1;
curwin->w_cursor.col = 0;
continue;
@@ -2492,7 +2658,7 @@ int get_c_indent(void)
*/ /* XXX */
n = FALSE;
if (lookfor != LOOKFOR_TERM && curbuf->b_ind_cpp_baseclass > 0) {
- n = cin_is_cpp_baseclass(&col);
+ n = cin_is_cpp_baseclass(&cache_cpp_baseclass);
l = get_cursor_line_ptr();
}
if (n) {
@@ -2508,7 +2674,7 @@ int get_c_indent(void)
continue;
} else
/* XXX */
- amount = get_baseclass_amount(col);
+ amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col);
break;
} else if (lookfor == LOOKFOR_CPP_BASECLASS) {
/* only look, whether there is a cpp base class
@@ -2550,23 +2716,32 @@ int get_c_indent(void)
amount = get_indent();
break;
}
- if (lookfor == LOOKFOR_NO_COMMA) {
- if (terminated != ',') {
- // Line below current line is the one that starts a
- // (possibly broken) line ending in a comma.
+ if (lookfor == LOOKFOR_COMMA) {
+ if (tryposBrace != NULL && tryposBrace->lnum
+ >= curwin->w_cursor.lnum) {
break;
}
- amount = get_indent();
- if (curwin->w_cursor.lnum - 1 == ourscope) {
- // line above is start of the scope, thus current line
- // is the one that stars a (possibly broken) line
- // ending in a comma.
+ if (terminated == ',') {
+ // Line below current line is the one that starts a
+ // (possibly broken) line ending in a comma.
break;
+ } else {
+ amount = get_indent();
+ if (curwin->w_cursor.lnum - 1 == ourscope) {
+ // line above is start of the scope, thus current
+ // line is the one that stars a (possibly broken)
+ // line ending in a comma.
+ break;
+ }
}
}
if (terminated == 0 || (lookfor != LOOKFOR_UNTERM
&& terminated == ',')) {
+ 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
// that starts it so we can get the right prevailing indent
// if ( foo &&
@@ -2772,30 +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 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.
- */
+ 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 (lookfor == LOOKFOR_INITIAL && terminated == ',') {
if (curbuf->b_ind_js) {
// Search for a line ending in a comma
@@ -2809,8 +2989,10 @@ int get_c_indent(void)
// 4 *
// 5,
// 6,
- lookfor = LOOKFOR_NO_COMMA;
- amount = get_indent(); // XXX
+ if (cin_iscomment(skipwhite(l))) {
+ break;
+ }
+ lookfor = LOOKFOR_COMMA;
trypos = find_match_char('[', curbuf->b_ind_maxparen);
if (trypos != NULL) {
if (trypos->lnum == curwin->w_cursor.lnum - 1) {
@@ -2831,7 +3013,9 @@ int get_c_indent(void)
// XXX
cont_amount = cin_get_equal_amount( curwin->w_cursor.lnum);
}
- if (lookfor != LOOKFOR_TERM && lookfor != LOOKFOR_JS_KEY) {
+ if (lookfor != LOOKFOR_TERM
+ && lookfor != LOOKFOR_JS_KEY
+ && lookfor != LOOKFOR_COMMA) {
lookfor = LOOKFOR_UNTERM;
}
}
@@ -3038,252 +3222,257 @@ term_again:
/* subtract extra left-shift for jump labels */
if (curbuf->b_ind_jump_label > 0 && original_line_islabel)
amount -= curbuf->b_ind_jump_label;
+
+ goto theend;
}
- else {
- // Ok -- we're not inside any sort of structure at all!
- //
- // this means we're at the top level, and everything should
- // basically just match where the previous line is, except
- // for the lines immediately following a function declaration,
- // which are K&R-style parameters and need to be indented.
-
- // if our line starts with an open brace, forget about any
- // prevailing indent and make sure it looks like the start
- // of a function
- if (theline[0] == '{') {
- amount = curbuf->b_ind_first_open;
- }
- /*
- * If the NEXT line is a function declaration, the current
- * line needs to be indented as a function type spec.
- * Don't do this if the current line looks like a comment or if the
- * current line is terminated, ie. ends in ';', or if the current line
- * contains { or }: "void f() {\n if (1)"
- */
- else if (cur_curpos.lnum < curbuf->b_ml.ml_line_count
- && !cin_nocode(theline)
- && vim_strchr(theline, '{') == NULL
- && vim_strchr(theline, '}') == NULL
- && !cin_ends_in(theline, (char_u *)":", NULL)
- && !cin_ends_in(theline, (char_u *)",", NULL)
- && cin_isfuncdecl(NULL, cur_curpos.lnum + 1,
- cur_curpos.lnum + 1)
- && !cin_isterminated(theline, FALSE, TRUE)) {
- amount = curbuf->b_ind_func_type;
- } else {
- amount = 0;
- curwin->w_cursor = cur_curpos;
- /* search backwards until we find something we recognize */
+ // Ok -- we're not inside any sort of structure at all!
+ //
+ // this means we're at the top level, and everything should
+ // basically just match where the previous line is, except
+ // for the lines immediately following a function declaration,
+ // which are K&R-style parameters and need to be indented.
- while (curwin->w_cursor.lnum > 1) {
- curwin->w_cursor.lnum--;
- curwin->w_cursor.col = 0;
+ // if our line starts with an open brace, forget about any
+ // prevailing indent and make sure it looks like the start
+ // of a function
- l = get_cursor_line_ptr();
+ if (theline[0] == '{') {
+ amount = curbuf->b_ind_first_open;
+ goto theend;
+ }
+ /*
+ * If the NEXT line is a function declaration, the current
+ * line needs to be indented as a function type spec.
+ * Don't do this if the current line looks like a comment or if the
+ * current line is terminated, ie. ends in ';', or if the current line
+ * contains { or }: "void f() {\n if (1)"
+ */
+ if (cur_curpos.lnum < curbuf->b_ml.ml_line_count
+ && !cin_nocode(theline)
+ && vim_strchr(theline, '{') == NULL
+ && vim_strchr(theline, '}') == NULL
+ && !cin_ends_in(theline, (char_u *)":", NULL)
+ && !cin_ends_in(theline, (char_u *)",", NULL)
+ && cin_isfuncdecl(NULL, cur_curpos.lnum + 1,
+ cur_curpos.lnum + 1)
+ && !cin_isterminated(theline, false, true)) {
+ amount = curbuf->b_ind_func_type;
+ goto theend;
+ }
- /*
- * If we're in a comment now, skip to the start of the comment.
- */ /* XXX */
- if ((trypos = ind_find_start_comment()) != NULL) {
- curwin->w_cursor.lnum = trypos->lnum + 1;
- curwin->w_cursor.col = 0;
- continue;
- }
+ /* search backwards until we find something we recognize */
+ amount = 0;
+ curwin->w_cursor = cur_curpos;
+ while (curwin->w_cursor.lnum > 1) {
+ curwin->w_cursor.lnum--;
+ curwin->w_cursor.col = 0;
- /*
- * Are we at the start of a cpp base class declaration or
- * constructor initialization?
- */ /* XXX */
- n = FALSE;
- if (curbuf->b_ind_cpp_baseclass != 0 && theline[0] != '{') {
- n = cin_is_cpp_baseclass(&col);
- l = get_cursor_line_ptr();
- }
- if (n) {
- /* XXX */
- amount = get_baseclass_amount(col);
- break;
- }
+ l = get_cursor_line_ptr();
- /*
- * Skip preprocessor directives and blank lines.
- */
- if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum))
- continue;
+ /*
+ * If we're in a comment or raw string now, skip to the start
+ * of it.
+ */ /* XXX */
+ if ((trypos = ind_find_start_CORS()) != NULL) {
+ curwin->w_cursor.lnum = trypos->lnum + 1;
+ curwin->w_cursor.col = 0;
+ continue;
+ }
- if (cin_nocode(l))
- continue;
+ /*
+ * Are we at the start of a cpp base class declaration or
+ * constructor initialization?
+ */ /* XXX */
+ n = false;
+ if (curbuf->b_ind_cpp_baseclass != 0 && theline[0] != '{') {
+ n = cin_is_cpp_baseclass(&cache_cpp_baseclass);
+ l = get_cursor_line_ptr();
+ }
+ if (n) {
+ /* XXX */
+ amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col);
+ break;
+ }
- /*
- * If the previous line ends in ',', use one level of
- * indentation:
- * int foo,
- * bar;
- * do this before checking for '}' in case of eg.
- * enum foobar
- * {
- * ...
- * } foo,
- * bar;
- */
- n = 0;
- if (cin_ends_in(l, (char_u *)",", NULL)
- || (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\')) {
- /* take us back to opening paren */
- if (find_last_paren(l, '(', ')')
- && (trypos = find_match_paren(
- curbuf->b_ind_maxparen)) != NULL)
- curwin->w_cursor = *trypos;
-
- /* For a line ending in ',' that is a continuation line go
- * back to the first line with a backslash:
- * char *foo = "bla\
- * bla",
- * here;
- */
- while (n == 0 && curwin->w_cursor.lnum > 1) {
- l = ml_get(curwin->w_cursor.lnum - 1);
- if (*l == NUL || l[STRLEN(l) - 1] != '\\')
- break;
- --curwin->w_cursor.lnum;
- curwin->w_cursor.col = 0;
- }
+ /*
+ * Skip preprocessor directives and blank lines.
+ */
+ if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum))
+ continue;
- amount = get_indent(); /* XXX */
+ if (cin_nocode(l))
+ continue;
- if (amount == 0)
- amount = cin_first_id_amount();
- if (amount == 0)
- amount = ind_continuation;
+ /*
+ * If the previous line ends in ',', use one level of
+ * indentation:
+ * int foo,
+ * bar;
+ * do this before checking for '}' in case of eg.
+ * enum foobar
+ * {
+ * ...
+ * } foo,
+ * bar;
+ */
+ n = 0;
+ if (cin_ends_in(l, (char_u *)",", NULL)
+ || (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\')) {
+ /* take us back to opening paren */
+ if (find_last_paren(l, '(', ')')
+ && (trypos = find_match_paren(
+ curbuf->b_ind_maxparen)) != NULL)
+ curwin->w_cursor = *trypos;
+
+ /* For a line ending in ',' that is a continuation line go
+ * back to the first line with a backslash:
+ * char *foo = "bla\
+ * bla",
+ * here;
+ */
+ while (n == 0 && curwin->w_cursor.lnum > 1) {
+ l = ml_get(curwin->w_cursor.lnum - 1);
+ if (*l == NUL || l[STRLEN(l) - 1] != '\\')
break;
- }
+ --curwin->w_cursor.lnum;
+ curwin->w_cursor.col = 0;
+ }
- /*
- * If the line looks like a function declaration, and we're
- * not in a comment, put it the left margin.
- */
- if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0)) /* XXX */
- break;
- l = get_cursor_line_ptr();
+ amount = get_indent(); /* XXX */
- /*
- * Finding the closing '}' of a previous function. Put
- * current line at the left margin. For when 'cino' has "fs".
- */
- if (*skipwhite(l) == '}')
- break;
+ if (amount == 0)
+ amount = cin_first_id_amount();
+ if (amount == 0)
+ amount = ind_continuation;
+ break;
+ }
- /* (matching {)
- * If the previous line ends on '};' (maybe followed by
- * comments) align at column 0. For example:
- * char *string_array[] = { "foo",
- * / * x * / "b};ar" }; / * foobar * /
- */
- if (cin_ends_in(l, (char_u *)"};", NULL))
- break;
+ /*
+ * If the line looks like a function declaration, and we're
+ * not in a comment, put it the left margin.
+ */
+ if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0)) /* XXX */
+ break;
+ l = get_cursor_line_ptr();
- // If the previous line ends on '[' we are probably in an
- // array constant:
- // something = [
- // 234, <- extra indent
- if (cin_ends_in(l, (char_u *)"[", NULL)) {
- amount = get_indent() + ind_continuation;
- break;
- }
+ /*
+ * Finding the closing '}' of a previous function. Put
+ * current line at the left margin. For when 'cino' has "fs".
+ */
+ if (*skipwhite(l) == '}')
+ break;
- /*
- * Find a line only has a semicolon that belongs to a previous
- * line ending in '}', e.g. before an #endif. Don't increase
- * indent then.
- */
- if (*(look = skipwhite(l)) == ';' && cin_nocode(look + 1)) {
- pos_T curpos_save = curwin->w_cursor;
+ /* (matching {)
+ * If the previous line ends on '};' (maybe followed by
+ * comments) align at column 0. For example:
+ * char *string_array[] = { "foo",
+ * / * x * / "b};ar" }; / * foobar * /
+ */
+ if (cin_ends_in(l, (char_u *)"};", NULL))
+ break;
- while (curwin->w_cursor.lnum > 1) {
- look = ml_get(--curwin->w_cursor.lnum);
- if (!(cin_nocode(look) || cin_ispreproc_cont(
- &look, &curwin->w_cursor.lnum)))
- break;
- }
- if (curwin->w_cursor.lnum > 0
- && cin_ends_in(look, (char_u *)"}", NULL))
- break;
+ // If the previous line ends on '[' we are probably in an
+ // array constant:
+ // something = [
+ // 234, <- extra indent
+ if (cin_ends_in(l, (char_u *)"[", NULL)) {
+ amount = get_indent() + ind_continuation;
+ break;
+ }
- curwin->w_cursor = curpos_save;
- }
+ /*
+ * Find a line only has a semicolon that belongs to a previous
+ * line ending in '}', e.g. before an #endif. Don't increase
+ * indent then.
+ */
+ if (*(look = skipwhite(l)) == ';' && cin_nocode(look + 1)) {
+ pos_T curpos_save = curwin->w_cursor;
- /*
- * If the PREVIOUS line is a function declaration, the current
- * line (and the ones that follow) needs to be indented as
- * parameters.
- */
- if (cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) {
- amount = curbuf->b_ind_param;
+ while (curwin->w_cursor.lnum > 1) {
+ look = ml_get(--curwin->w_cursor.lnum);
+ if (!(cin_nocode(look) || cin_ispreproc_cont(
+ &look, &curwin->w_cursor.lnum)))
break;
- }
+ }
+ if (curwin->w_cursor.lnum > 0
+ && cin_ends_in(look, (char_u *)"}", NULL))
+ break;
- /*
- * If the previous line ends in ';' and the line before the
- * previous line ends in ',' or '\', ident to column zero:
- * int foo,
- * bar;
- * indent_to_0 here;
- */
- if (cin_ends_in(l, (char_u *)";", NULL)) {
- l = ml_get(curwin->w_cursor.lnum - 1);
- if (cin_ends_in(l, (char_u *)",", NULL)
- || (*l != NUL && l[STRLEN(l) - 1] == '\\'))
- break;
- l = get_cursor_line_ptr();
- }
+ curwin->w_cursor = curpos_save;
+ }
- /*
- * Doesn't look like anything interesting -- so just
- * use the indent of this line.
- *
- * Position the cursor over the rightmost paren, so that
- * matching it will take us back to the start of the line.
- */
- find_last_paren(l, '(', ')');
+ /*
+ * If the PREVIOUS line is a function declaration, the current
+ * line (and the ones that follow) needs to be indented as
+ * parameters.
+ */
+ if (cin_isfuncdecl(&l, curwin->w_cursor.lnum, 0)) {
+ amount = curbuf->b_ind_param;
+ break;
+ }
- if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL)
- curwin->w_cursor = *trypos;
- amount = get_indent(); /* XXX */
+ /*
+ * If the previous line ends in ';' and the line before the
+ * previous line ends in ',' or '\', ident to column zero:
+ * int foo,
+ * bar;
+ * indent_to_0 here;
+ */
+ if (cin_ends_in(l, (char_u *)";", NULL)) {
+ l = ml_get(curwin->w_cursor.lnum - 1);
+ if (cin_ends_in(l, (char_u *)",", NULL)
+ || (*l != NUL && l[STRLEN(l) - 1] == '\\'))
break;
- }
+ l = get_cursor_line_ptr();
+ }
- /* add extra indent for a comment */
- if (cin_iscomment(theline))
- amount += curbuf->b_ind_comment;
+ /*
+ * Doesn't look like anything interesting -- so just
+ * use the indent of this line.
+ *
+ * Position the cursor over the rightmost paren, so that
+ * matching it will take us back to the start of the line.
+ */
+ find_last_paren(l, '(', ')');
- /* add extra indent if the previous line ended in a backslash:
- * "asdfasdf\
- * here";
- * char *foo = "asdf\
- * here";
- */
- if (cur_curpos.lnum > 1) {
- l = ml_get(cur_curpos.lnum - 1);
- if (*l != NUL && l[STRLEN(l) - 1] == '\\') {
- cur_amount = cin_get_equal_amount(cur_curpos.lnum - 1);
- if (cur_amount > 0)
- amount = cur_amount;
- else if (cur_amount == 0)
- amount += ind_continuation;
- }
- }
+ if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL)
+ curwin->w_cursor = *trypos;
+ amount = get_indent(); /* XXX */
+ break;
+ }
+
+ /* add extra indent for a comment */
+ if (cin_iscomment(theline))
+ amount += curbuf->b_ind_comment;
+
+ /* add extra indent if the previous line ended in a backslash:
+ * "asdfasdf\
+ * here";
+ * char *foo = "asdf\
+ * here";
+ */
+ if (cur_curpos.lnum > 1) {
+ l = ml_get(cur_curpos.lnum - 1);
+ if (*l != NUL && l[STRLEN(l) - 1] == '\\') {
+ cur_amount = cin_get_equal_amount(cur_curpos.lnum - 1);
+ if (cur_amount > 0)
+ amount = cur_amount;
+ else if (cur_amount == 0)
+ amount += ind_continuation;
}
}
theend:
+ if (amount < 0)
+ amount = 0;
+
+laterend:
/* put the cursor back where it belongs */
curwin->w_cursor = cur_curpos;
xfree(linecopy);
- if (amount < 0)
- return 0;
return amount;
}
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index 04d6cbf5e3..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>
@@ -18,6 +13,9 @@
#include "nvim/strings.h"
#include "nvim/mouse.h"
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "keymap.c.generated.h"
+#endif
/*
* Some useful tables.
@@ -36,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}
};
@@ -483,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) {
@@ -515,70 +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 */
+ // 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 (has_mbyte)
- l = mb_ptr2len(bp + 1);
- else
+ if (bp + 1 <= end) {
+ if (has_mbyte) {
+ 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) {
- vim_str2nr(bp + 5, NULL, &l, TRUE, TRUE, NULL, NULL);
+ 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? */
@@ -586,62 +595,60 @@ find_special_key (
for (bp = src + 1; bp < last_dash; bp++) {
if (*bp != '-') {
bit = name_to_mod_mask(*bp);
- if (bit == 0x0)
- break; /* Illegal modifier name */
+ if (bit == 0x0) {
+ break; // Illegal modifier name
+ }
modifiers |= bit;
}
}
- /*
- * Legal modifier name.
- */
+ // Legal modifier name.
if (bp >= last_dash) {
if (STRNICMP(last_dash + 1, "char-", 5) == 0
&& ascii_isdigit(last_dash[6])) {
- /* <Char-123> or <Char-033> or <Char-0x33> */
- vim_str2nr(last_dash + 6, NULL, NULL, TRUE, TRUE, NULL, &n);
+ // <Char-123> or <Char-033> or <Char-0x33>
+ vim_str2nr(last_dash + 6, NULL, NULL, STR2NR_ALL, NULL, &n, 0);
key = (int)n;
} else {
/*
* Modifier with single letter, or special key name.
*/
- if (has_mbyte)
+ if (has_mbyte) {
l = mb_ptr2len(last_dash + 1);
- else
+ } else {
l = 1;
- if (modifiers != 0 && last_dash[l + 1] == '>')
+ }
+ if (modifiers != 0 && last_dash[l + 1] == '>') {
key = PTR2CHAR(last_dash + 1);
- else {
+ } else {
key = get_special_key_code(last_dash + 1);
- if (!keep_x_key)
+ if (!keep_x_key) {
key = handle_x_keys(key);
+ }
}
}
- /*
- * get_special_key_code() may return NUL for invalid
- * special key name.
- */
+ // get_special_key_code() may return NUL for invalid
+ // special key name.
if (key != NUL) {
- /*
- * Only use a modifier when there is no special key code that
- * includes the modifier.
- */
+ // Only use a modifier when there is no special key code that
+ // includes the modifier.
key = simplify_key(key, &modifiers);
if (!keycode) {
- /* don't want keycode, use single byte code */
- if (key == K_BS)
+ // don't want keycode, use single byte code
+ if (key == K_BS) {
key = BS;
- else if (key == K_DEL || key == K_KDEL)
+ } else if (key == K_DEL || key == K_KDEL) {
key = DEL;
+ }
}
- /*
- * Normal Key with modifier: Try to make a single byte code.
- */
- if (!IS_SPECIAL(key))
+ // Normal Key with modifier:
+ // Try to make a single byte code (except for Alt/Meta modifiers).
+ if (!IS_SPECIAL(key)) {
key = extract_modifiers(key, &modifiers);
+ }
*modp = modifiers;
*srcp = end_of_name;
@@ -652,32 +659,25 @@ find_special_key (
return 0;
}
-/*
- * Try to include modifiers in the key.
- * Changes "Shift-a" to 'A', "Alt-A" to 0xc0, etc.
- */
-int extract_modifiers(int key, int *modp)
+/// Try to include modifiers (except alt/meta) in the key.
+/// Changes "Shift-a" to 'A', "Ctrl-@" to <Nul>, etc.
+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))
- ) {
+ && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) {
key = Ctrl_chr(key);
modifiers &= ~MOD_MASK_CTRL;
- /* <C-@> is <Nul> */
- if (key == 0)
+ if (key == 0) { // <C-@> is <Nul>
key = K_ZERO;
- }
- if ((modifiers & MOD_MASK_ALT) && key < 0x80
- && !enc_dbcs /* avoid creating a lead byte */
- ) {
- key |= 0x80;
- modifiers &= ~MOD_MASK_ALT; /* remove the META modifier */
+ }
}
*modp = modifiers;
@@ -707,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;
@@ -741,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') {
@@ -796,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 {
@@ -817,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;
@@ -830,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 {
@@ -862,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;
}
@@ -872,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/log.c b/src/nvim/log.c
index 5767da03af..773d497881 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -10,10 +10,6 @@
#include "nvim/os/os.h"
#include "nvim/os/time.h"
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
#define USR_LOG_FILE "$HOME" _PATHSEPSTR ".nvimlog"
static uv_mutex_t mutex;
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 cef10d12d5..71a972e8f6 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -219,9 +219,10 @@ int main(int argc, char **argv)
{
argv0 = (char *)path_tail((char_u *)argv[0]);
- char_u *fname = NULL; /* file name from command line */
- mparm_T params; /* various parameters passed between
- * main() and other functions. */
+ char_u *fname = NULL; // file name from command line
+ mparm_T params; // various parameters passed between
+ // main() and other functions.
+ char_u *cwd = NULL; // current workding dir on startup
time_init();
/* Many variables are in "params" so that we can pass them to invoked
@@ -237,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();
/*
@@ -316,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);
@@ -331,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.
@@ -461,11 +472,10 @@ int main(int argc, char **argv)
TIME_MSG("jump to first error");
}
- /*
- * If opened more than one window, start editing files in the other
- * windows.
- */
- edit_buffers(&params);
+ // If opened more than one window, start editing files in the other
+ // windows.
+ edit_buffers(&params, cwd);
+ xfree(cwd);
if (params.diff_mode) {
/* set options in each window for "nvim -d". */
@@ -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");
}
@@ -1182,12 +1198,19 @@ static char_u *get_fname(mparm_T *parmp)
* Expand wildcards in file names.
*/
if (!parmp->literal) {
- /* Temporarily add '(' and ')' to 'isfname'. These are valid
- * filename characters but are excluded from 'isfname' to make
- * "gf" work on a file name in parenthesis (e.g.: see vim.h). */
+ cwd = xmalloc(MAXPATHL);
+ if (cwd != NULL) {
+ os_dirname(cwd, MAXPATHL);
+ }
+ // Temporarily add '(' and ')' to 'isfname'. These are valid
+ // filename characters but are excluded from 'isfname' to make
+ // "gf" work on a file name in parenthesis (e.g.: see vim.h).
do_cmdline_cmd(":set isf+=(,)");
alist_expand(NULL, 0);
do_cmdline_cmd(":set isf&");
+ if (cwd != NULL) {
+ os_chdir((char *)cwd);
+ }
}
#endif
return alist_name(&GARGLIST[0]);
@@ -1417,11 +1440,9 @@ static void create_windows(mparm_T *parmp)
}
}
-/*
- * If opened more than one window, start editing files in the other
- * windows. make_windows() has already opened the windows.
- */
-static void edit_buffers(mparm_T *parmp)
+/// If opened more than one window, start editing files in the other
+/// windows. make_windows() has already opened the windows.
+static void edit_buffers(mparm_T *parmp, char_u *cwd)
{
int arg_idx; /* index in argument list */
int i;
@@ -1442,7 +1463,10 @@ static void edit_buffers(mparm_T *parmp)
arg_idx = 1;
for (i = 1; i < parmp->window_count; ++i) {
- /* When w_arg_idx is -1 remove the window (see create_windows()). */
+ if (cwd != NULL) {
+ os_chdir((char *)cwd);
+ }
+ // When w_arg_idx is -1 remove the window (see create_windows()).
if (curwin->w_arg_idx == -1) {
++arg_idx;
win_close(curwin, TRUE);
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 b02c18c53b..3495203c43 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -568,16 +568,11 @@ char_u * mb_init(void)
/* When enc_utf8 is set or reset, (de)allocate ScreenLinesUC[] */
screenalloc(false);
- /* When using Unicode, set default for 'fileencodings'. */
- if (enc_utf8 && !option_was_set((char_u *)"fencs"))
- set_string_option_direct((char_u *)"fencs", -1,
- (char_u *)"ucs-bom,utf-8,default,latin1", OPT_FREE, 0);
-
#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
@@ -1309,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
}
/*
@@ -2417,11 +2415,8 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET
int i;
if (STRCMP(enc, "default") == 0) {
- /* Use the default encoding as it's found by set_init_1(). */
- char_u *r = get_encoding_default();
- if (r == NULL)
- r = (char_u *)"latin1";
- return vim_strsave(r);
+ // Use the default encoding as found by set_init_1().
+ return vim_strsave(fenc_default);
}
/* copy "enc" to allocated memory, with room for two '-' */
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index 3df4cbc4a3..6599db787f 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -40,6 +40,7 @@
#include <limits.h>
#include <string.h>
#include <stdbool.h>
+#include <fcntl.h>
#include "nvim/vim.h"
#include "nvim/ascii.h"
@@ -78,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
@@ -87,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
}
}
@@ -114,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.
@@ -170,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.
@@ -184,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);
}
@@ -215,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);
}
}
@@ -389,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
@@ -459,32 +457,10 @@ int mf_sync(memfile_T *mfp, int flags)
if (hp == NULL || status == FAIL)
mfp->mf_dirty = false;
- if ((flags & MFS_FLUSH) && *p_sws != NUL) {
-#if defined(UNIX)
-# ifdef HAVE_FSYNC
- if (STRCMP(p_sws, "fsync") == 0) {
- if (fsync(mfp->mf_fd))
- status = FAIL;
- } else {
-# endif
- // OpenNT is strictly POSIX (Benzinger).
- // Tandem/Himalaya NSK-OSS doesn't have sync()
-# if defined(__OPENNT) || defined(__TANDEM)
- fflush(NULL);
-# else
- sync();
-# endif
-# ifdef HAVE_FSYNC
+ if (flags & MFS_FLUSH) {
+ if (os_fsync(mfp->mf_fd)) {
+ status = FAIL;
}
-# endif
-#endif
-# ifdef SYNC_DUP_CLOSE
- // Win32 is a bit more work: Duplicate the file handle and close it.
- // This should flush the file to disk.
- int fd;
- if ((fd = dup(mfp->mf_fd)) >= 0)
- close(fd);
-# endif
}
got_int |= got_int_save;
@@ -496,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;
}
@@ -527,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;
}
@@ -636,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) {
@@ -773,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
@@ -794,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.
@@ -877,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);
}
@@ -899,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;
@@ -910,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.
@@ -925,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) {
+ 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;
}
//
@@ -969,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 0ba8dd98d0..4e35dd481f 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -38,6 +38,7 @@
#include <inttypes.h>
#include <string.h>
#include <stdbool.h>
+#include <fcntl.h>
#include "nvim/ascii.h"
#include "nvim/vim.h"
@@ -64,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"
@@ -425,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;
}
@@ -788,9 +786,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;
@@ -3195,7 +3192,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
@@ -3954,8 +3951,10 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp)
if (ffdos)
size += lnum - 1;
- /* Don't count the last line break if 'bin' and 'noeol'. */
- if (buf->b_p_bin && !buf->b_p_eol && buf->b_ml.ml_line_count == lnum) {
+ /* Don't count the last line break if 'noeol' and ('bin' or
+ * 'nofixeol'). */
+ if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol
+ && buf->b_ml.ml_line_count == lnum) {
size -= ffdos + 1;
}
}
diff --git a/src/nvim/memline_defs.h b/src/nvim/memline_defs.h
index bcc1c673d2..34a002af5d 100644
--- a/src/nvim/memline_defs.h
+++ b/src/nvim/memline_defs.h
@@ -41,7 +41,7 @@ typedef struct memline {
int ml_flags;
infoptr_T *ml_stack; /* stack of pointer blocks (array of IPTRs) */
- int ml_stack_top; /* current top if ml_stack */
+ int ml_stack_top; /* current top of ml_stack */
int ml_stack_size; /* total number of entries in ml_stack */
linenr_T ml_line_lnum; /* line number of cached line, 0 if not valid */
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 66b8b9b5d2..265f8c00c0 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)
+/// 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, 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)
-{
- if (emsg_not_now()) {
- return TRUE; // no error messages at the moment
- }
-
- 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);
}
@@ -1396,7 +1366,8 @@ void msg_prt_line(char_u *s, int list)
c = *p_extra++;
} else if (has_mbyte && (l = (*mb_ptr2len)(s)) > 1) {
col += (*mb_ptr2cells)(s);
- if (lcs_nbsp != NUL && list && mb_ptr2char(s) == 160) {
+ if (lcs_nbsp != NUL && list
+ && (mb_ptr2char(s) == 160 || mb_ptr2char(s) == 0x202f)) {
mb_char2bytes(lcs_nbsp, buf);
buf[(*mb_ptr2len)(buf)] = NUL;
} else {
@@ -1600,39 +1571,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)
@@ -1739,18 +1702,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)
@@ -1786,25 +1746,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 */
@@ -2571,7 +2530,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)
@@ -3037,7 +2996,7 @@ static double tv_float(typval_T *tvs, int *idxp)
* http://www.ijs.si/software/snprintf/
*
* This snprintf() only supports the following conversion specifiers:
- * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
+ * s, c, b, B, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
* with flags: '-', '+', ' ', '0' and '#'.
* An asterisk is supported for field width as well as precision.
*
@@ -3085,7 +3044,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;
@@ -3096,15 +3055,17 @@ 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)
+ if (!p) {
p = "";
+ }
while (*p) {
if (*p != '%') {
// copy up to the next '%' or NUL without any changes
@@ -3133,7 +3094,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;
@@ -3176,9 +3137,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
if (*p == '*') {
p++;
int j = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int);
- if (j >= 0)
+ if (j >= 0) {
min_field_width = j;
- else {
+ } else {
min_field_width = -j;
justify_left = 1;
}
@@ -3187,8 +3148,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
// argument like common implementations do
unsigned int uj = *p++ - '0';
- while (ascii_isdigit((int)(*p)))
+ while (ascii_isdigit((int)(*p))) {
uj = 10 * uj + (unsigned int)(*p++ - '0');
+ }
min_field_width = uj;
}
@@ -3199,9 +3161,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
if (*p == '*') {
int j = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int);
p++;
- if (j >= 0)
+ if (j >= 0) {
precision = j;
- else {
+ } else {
precision_specified = 0;
precision = 0;
}
@@ -3210,8 +3172,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
// treat argument like common implementations do
unsigned int uj = *p++ - '0';
- while (ascii_isdigit((int)(*p)))
+ while (ascii_isdigit((int)(*p))) {
uj = 10 * uj + (unsigned int)(*p++ - '0');
+ }
precision = uj;
}
}
@@ -3262,14 +3225,13 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
if (!str_arg) {
str_arg = "[NULL]";
str_arg_l = 6;
- }
- // make sure not to address string beyond the specified precision
- else if (!precision_specified)
+ } else if (!precision_specified) {
+ // make sure not to address string beyond the specified precision
str_arg_l = strlen(str_arg);
- // truncate string if necessary as requested by precision
- else if (precision == 0)
+ } else if (precision == 0) {
+ // truncate string if necessary as requested by precision
str_arg_l = 0;
- else {
+ } else {
// memchr on HP does not like n > 2^31
// TODO(elmart): check if this still holds / is relevant
str_arg_l = (size_t)((char *)xmemscan(str_arg,
@@ -3283,8 +3245,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
- mb_string2cells((char_u *) str_arg);
if (precision) {
char_u *p1 = (char_u *)str_arg;
- for (size_t i = 0; i < precision && *p1; i++)
+ for (size_t i = 0; i < precision && *p1; i++) {
p1 += mb_ptr2len(p1);
+ }
str_arg_l = precision = p1 - (char_u *)str_arg;
}
}
@@ -3295,9 +3258,14 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
}
break;
- case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
- // u, o, x, X and p conversion specifiers imply the value is unsigned;
- // d implies a signed value
+ case 'd':
+ case 'u':
+ case 'b': case 'B':
+ case 'o':
+ case 'x': case 'X':
+ case 'p': {
+ // u, b, B, o, x, X and p conversion specifiers imply
+ // the value is unsigned; d implies a signed value
// 0 if numeric argument is zero (or if pointer is NULL for 'p'),
// +1 if greater than zero (or non NULL for 'p'),
@@ -3325,8 +3293,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
if (fmt_spec == 'p') {
length_modifier = '\0';
ptr_arg = tvs ? (void *)tv_str(tvs, &arg_idx) : va_arg(ap, void *);
- if (ptr_arg)
+ if (ptr_arg) {
arg_sign = 1;
+ }
} else if (fmt_spec == 'd') {
// signed
switch (length_modifier) {
@@ -3334,25 +3303,28 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
case 'h':
// char and short arguments are passed as int
int_arg = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int);
- if (int_arg > 0)
+ if (int_arg > 0) {
arg_sign = 1;
- else if (int_arg < 0)
+ } else if (int_arg < 0) {
arg_sign = -1;
+ }
break;
case 'l':
long_arg = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, long int);
- if (long_arg > 0)
+ if (long_arg > 0) {
arg_sign = 1;
- else if (long_arg < 0)
+ } else if (long_arg < 0) {
arg_sign = -1;
+ }
break;
case '2':
long_long_arg = tvs ? tv_nr(tvs, &arg_idx)
- : va_arg(ap, long long int);
- if (long_long_arg > 0)
+ : va_arg(ap, long long int); // NOLINT (runtime/int)
+ if (long_long_arg > 0) {
arg_sign = 1;
- else if (long_long_arg < 0)
+ } else if (long_long_arg < 0) {
arg_sign = -1;
+ }
break;
}
} else {
@@ -3362,24 +3334,23 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
case 'h':
uint_arg = tvs ? (unsigned)tv_nr(tvs, &arg_idx)
: va_arg(ap, unsigned int);
- if (uint_arg != 0)
- arg_sign = 1;
+ if (uint_arg != 0) { arg_sign = 1; }
break;
case 'l':
ulong_arg = tvs ? (unsigned long)tv_nr(tvs, &arg_idx)
: va_arg(ap, unsigned long int);
- if (ulong_arg != 0)
- arg_sign = 1;
+ if (ulong_arg != 0) { arg_sign = 1; }
break;
case '2':
- ulong_long_arg = tvs ? (unsigned long long)tv_nr(tvs, &arg_idx)
- : va_arg(ap, unsigned long long int);
- if (ulong_long_arg) arg_sign = 1;
+ ulong_long_arg = tvs
+ ? (unsigned long long)tv_nr(tvs, &arg_idx) // NOLINT (runtime/int)
+ : va_arg(ap, unsigned long long int); // NOLINT (runtime/int)
+ if (ulong_long_arg) { arg_sign = 1; }
break;
case 'z':
size_t_arg = tvs ? (size_t)tv_nr(tvs, &arg_idx)
: va_arg(ap, size_t);
- if (size_t_arg) arg_sign = 1;
+ if (size_t_arg) { arg_sign = 1; }
break;
}
}
@@ -3390,16 +3361,19 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
// For d, i, u, o, x, and X conversions, if precision is specified,
// '0' flag should be ignored. This is so with Solaris 2.6, Digital UNIX
// 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
- if (precision_specified)
+ if (precision_specified) {
zero_padding = 0;
+ }
if (fmt_spec == 'd') {
- if (force_sign && arg_sign >= 0)
+ if (force_sign && arg_sign >= 0) {
tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
+ }
// 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') ) {
+ 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;
}
@@ -3407,20 +3381,20 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
}
zero_padding_insertion_ind = str_arg_l;
- if (!precision_specified)
+ if (!precision_specified) {
precision = 1; // default precision is 1
+ }
if (precision == 0 && arg_sign == 0) {
// when zero value is formatted with an explicit precision 0,
- // resulting formatted string is empty (d, i, u, o, x, X, p)
+ // resulting formatted string is empty (d, i, u, b, B, o, x, X, p)
} else {
char f[5];
int f_l = 0;
// construct a simple format string for sprintf
f[f_l++] = '%';
- if (!length_modifier)
- ;
- else if (length_modifier == '2') {
+ if (!length_modifier) {
+ } else if (length_modifier == '2') {
f[f_l++] = 'l';
f[f_l++] = 'l';
} else
@@ -3441,6 +3415,41 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
case '2': str_arg_l += sprintf(tmp + str_arg_l, f, long_long_arg);
break;
}
+ } else if (fmt_spec == 'b' || fmt_spec == 'B') {
+ // binary
+ size_t bits = 0;
+ switch (length_modifier) {
+ case '\0':
+ case 'h': for (bits = sizeof(unsigned) * 8; bits > 0; bits--) {
+ if ((uint_arg >> (bits - 1)) & 0x1) { break; } }
+
+ while (bits > 0) {
+ tmp[str_arg_l++] =
+ ((uint_arg >> --bits) & 0x1) ? '1' : '0'; }
+ break;
+ case 'l': for (bits = sizeof(unsigned long) * 8; bits > 0; bits--) {
+ if ((ulong_arg >> (bits - 1)) & 0x1) { break; } }
+
+ while (bits > 0) {
+ tmp[str_arg_l++] =
+ ((ulong_arg >> --bits) & 0x1) ? '1' : '0'; }
+ break;
+ case '2': for (bits = sizeof(unsigned long long) * 8; // NOLINT (runtime/int)
+ bits > 0; bits--) {
+ if ((ulong_long_arg >> (bits - 1)) & 0x1) { break; } }
+
+ while (bits > 0) {
+ tmp[str_arg_l++] =
+ ((ulong_long_arg >> --bits) & 0x1) ? '1' : '0'; }
+ break;
+ case 'z': for (bits = sizeof(size_t) * 8; bits > 0; bits--) {
+ if ((size_t_arg >> (bits - 1)) & 0x1) { break; } }
+
+ while (bits > 0) {
+ tmp[str_arg_l++] =
+ ((size_t_arg >> --bits) & 0x1) ? '1' : '0'; }
+ break;
+ }
} else {
// unsigned
switch (length_modifier) {
@@ -3464,7 +3473,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
if (zero_padding_insertion_ind + 1 < str_arg_l
&& tmp[zero_padding_insertion_ind] == '0'
&& (tmp[zero_padding_insertion_ind + 1] == 'x'
- || tmp[zero_padding_insertion_ind + 1] == 'X'))
+ || tmp[zero_padding_insertion_ind + 1] == 'X'
+ || tmp[zero_padding_insertion_ind + 1] == 'b'
+ || tmp[zero_padding_insertion_ind + 1] == 'B'))
zero_padding_insertion_ind += 2;
}
@@ -3507,7 +3518,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
// floating point
char format[40];
int l;
- int remove_trailing_zeroes = FALSE;
+ int remove_trailing_zeroes = false;
double f = tvs ? tv_float(tvs, &arg_idx) : va_arg(ap, double);
double abs_f = f < 0 ? -f : f;
@@ -3518,7 +3529,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
fmt_spec = 'f';
else
fmt_spec = fmt_spec == 'g' ? 'e' : 'E';
- remove_trailing_zeroes = TRUE;
+ remove_trailing_zeroes = true;
}
if (fmt_spec == 'f' && abs_f > 1.0e307) {
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 96ef6cbaef..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) {
@@ -2352,7 +2307,7 @@ int get_keystroke(void)
int save_mapped_ctrl_c = mapped_ctrl_c;
int waited = 0;
- mapped_ctrl_c = FALSE; /* mappings are not used here */
+ mapped_ctrl_c = 0; // mappings are not used here
for (;; ) {
// flush output before waiting
ui_flush();
@@ -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,45 +2684,33 @@ void fast_breakcheck(void)
}
}
-#ifndef SEEK_SET
-# define SEEK_SET 0
-#endif
-#ifndef SEEK_END
-# define SEEK_END 2
-#endif
-
-/*
- * 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).
@@ -2779,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);
@@ -2790,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..4b64de1be0 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;
}
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/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 a119c4653a..0049ae6b95 100644
--- a/src/nvim/msgpack_rpc/helpers.c
+++ b/src/nvim/msgpack_rpc/helpers.c
@@ -94,10 +94,9 @@ bool msgpack_rpc_to_string(msgpack_object *obj, String *arg)
FUNC_ATTR_NONNULL_ALL
{
if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) {
- if (obj->via.bin.ptr == NULL) {
- return false;
- }
- arg->data = xmemdupz(obj->via.bin.ptr, obj->via.bin.size);
+ arg->data = obj->via.bin.ptr != NULL
+ ? xmemdupz(obj->via.bin.ptr, obj->via.bin.size)
+ : NULL;
arg->size = obj->via.bin.size;
return true;
}
@@ -420,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/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 ad53e9bf24..f5607f3676 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)
@@ -1205,9 +1202,9 @@ static void normal_check_interrupt(NormalState *s)
static void normal_check_cursor_moved(NormalState *s)
{
// Trigger CursorMoved if the cursor moved.
- if (!finish_op && (has_cursormoved() || curwin->w_p_cole > 0)
+ if (!finish_op && (has_event(EVENT_CURSORMOVED) || curwin->w_p_cole > 0)
&& !equalpos(last_cursormoved, curwin->w_cursor)) {
- if (has_cursormoved()) {
+ if (has_event(EVENT_CURSORMOVED)) {
apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
}
@@ -1224,7 +1221,7 @@ static void normal_check_cursor_moved(NormalState *s)
static void normal_check_text_changed(NormalState *s)
{
// Trigger TextChanged if b_changedtick differs.
- if (!finish_op && has_textchanged()
+ if (!finish_op && has_event(EVENT_TEXTCHANGED)
&& last_changedtick != curbuf->b_changedtick) {
if (last_changedtick_buf == curbuf) {
apply_autocmds(EVENT_TEXTCHANGED, NULL, NULL, false, curbuf);
@@ -1414,11 +1411,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
int lbr_saved = curwin->w_p_lbr;
- /* The visual area is remembered for redo */
- static int redo_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */
- static linenr_T redo_VIsual_line_count; /* number of lines */
- static colnr_T redo_VIsual_vcol; /* number of cols or end column */
- static long redo_VIsual_count; /* count for Visual operator */
+ // The visual area is remembered for redo
+ static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V
+ static linenr_T redo_VIsual_line_count; // number of lines
+ static colnr_T redo_VIsual_vcol; // number of cols or end column
+ static long redo_VIsual_count; // count for Visual operator
+ static int redo_VIsual_arg; // extra argument
bool include_line_break = false;
old_cursor = curwin->w_cursor;
@@ -1430,21 +1428,25 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
|| VIsual_active
) && oap->op_type != OP_NOP) {
// Avoid a problem with unwanted linebreaks in block mode
+ if (curwin->w_p_lbr) {
+ curwin->w_valid &= ~VALID_VIRTCOL;
+ }
curwin->w_p_lbr = false;
oap->is_VIsual = VIsual_active;
- if (oap->motion_force == 'V')
- oap->motion_type = 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;
@@ -1534,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 =
@@ -1580,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;
@@ -1598,55 +1604,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
virtual_op = virtual_active();
if (VIsual_active || redo_VIsual_busy) {
- if (VIsual_mode == Ctrl_V) { /* block mode */
- colnr_T start, end;
-
- oap->block_mode = true;
-
- getvvcol(curwin, &(oap->start),
- &oap->start_vcol, NULL, &oap->end_vcol);
- if (!redo_VIsual_busy) {
- getvvcol(curwin, &(oap->end), &start, NULL, &end);
-
- if (start < oap->start_vcol)
- oap->start_vcol = start;
- if (end > oap->end_vcol) {
- if (*p_sel == 'e' && start >= 1
- && start - 1 >= oap->end_vcol)
- oap->end_vcol = start - 1;
- else
- oap->end_vcol = end;
- }
- }
-
- /* if '$' was used, get oap->end_vcol from longest line */
- if (curwin->w_curswant == MAXCOL) {
- curwin->w_cursor.col = MAXCOL;
- oap->end_vcol = 0;
- for (curwin->w_cursor.lnum = oap->start.lnum;
- curwin->w_cursor.lnum <= oap->end.lnum;
- ++curwin->w_cursor.lnum) {
- getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
- if (end > oap->end_vcol)
- oap->end_vcol = end;
- }
- } else if (redo_VIsual_busy)
- oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1;
- /*
- * Correct oap->end.col and oap->start.col to be the
- * upper-left and lower-right corner of the block area.
- *
- * (Actually, this does convert column positions into character
- * positions)
- */
- curwin->w_cursor.lnum = oap->end.lnum;
- coladvance(oap->end_vcol);
- oap->end = curwin->w_cursor;
-
- curwin->w_cursor = oap->start;
- coladvance(oap->start_vcol);
- oap->start = curwin->w_cursor;
- }
+ get_op_vcol(oap, redo_VIsual_vcol, true);
if (!redo_VIsual_busy && !gui_yank) {
/*
@@ -1701,38 +1659,33 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
redo_VIsual_vcol = resel_VIsual_vcol;
redo_VIsual_line_count = resel_VIsual_line_count;
redo_VIsual_count = cap->count0;
+ redo_VIsual_arg = cap->arg;
}
}
- /*
- * oap->inclusive defaults to true.
- * If oap->end is on a NUL (empty line) oap->inclusive becomes
- * false. This makes "d}P" and "v}dP" work the same.
- */
- if (oap->motion_force == NUL || oap->motion_type == 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;
- else {
- oap->motion_type = MCHAR;
- if (VIsual_mode != Ctrl_V && *ml_get_pos(&(oap->end)) == NUL
+ }
+ if (VIsual_mode == 'V') {
+ oap->motion_type = kMTLineWise;
+ } else if (VIsual_mode == 'v') {
+ oap->motion_type = kMTCharWise;
+ if (*ml_get_pos(&(oap->end)) == NUL
&& (include_line_break || !virtual_op)
) {
oap->inclusive = false;
- /* Try to include the newline, unless it's an operator
- * that works on lines only. */
- if (*p_sel != 'o' && !op_on_lines(oap->op_type)) {
- 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++;
}
}
}
@@ -1750,10 +1703,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
VIsual_active = false;
setmouse();
mouse_dragging = 0;
- if (mode_displayed)
- clear_cmdline = true; /* unshow visual mode later */
- else
- clear_showcmd();
+ may_clear_cmdline();
if ((oap->op_type == OP_YANK
|| oap->op_type == OP_COLON
|| oap->op_type == OP_FUNCTION
@@ -1780,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 == MCHAR
+ oap->empty = (oap->motion_type != kMTLineWise
&& (!oap->inclusive
|| (oap->op_type == OP_YANK
&& gchar_pos(&oap->end) == NUL))
@@ -1805,24 +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
- && oap->inclusive == false
- && !(cap->retval & CA_NO_ADJ_OP_END)
- && oap->end.col == 0
- && (!oap->is_VIsual || *p_sel == 'o')
- && !oap->block_mode
- && 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 {
+ if (oap->motion_type == kMTCharWise
+ && oap->inclusive == false
+ && !(cap->retval & CA_NO_ADJ_OP_END)
+ && oap->end.col == 0
+ && (!oap->is_VIsual || *p_sel == 'o')
+ && oap->line_count > 1) {
+ oap->end_adjusted = true; // remember that we did this
+ oap->line_count--;
+ oap->end.lnum--;
+ if (inindent(0)) {
+ oap->motion_type = kMTLineWise;
+ } else {
oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
if (oap->end.col) {
--oap->end.col;
@@ -1861,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;
@@ -1895,9 +1845,14 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
else
restart_edit_save = 0;
restart_edit = 0;
+
// Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
- /* Reset finish_op now, don't want it set inside edit(). */
+ if (curwin->w_p_lbr != lbr_saved) {
+ curwin->w_p_lbr = lbr_saved;
+ get_op_vcol(oap, redo_VIsual_mode, false);
+ }
+
+ // Reset finish_op now, don't want it set inside edit().
finish_op = false;
if (op_change(oap)) /* will call edit() */
cap->retval |= CA_COMMAND_BUSY;
@@ -1975,7 +1930,10 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
restart_edit = 0;
// Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
+ if (curwin->w_p_lbr != lbr_saved) {
+ curwin->w_p_lbr = lbr_saved;
+ get_op_vcol(oap, redo_VIsual_mode, false);
+ }
op_insert(oap, cap->count1);
@@ -1998,7 +1956,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
CancelRedo();
} else {
// Restore linebreak, so that when the user edits it looks as before.
- curwin->w_p_lbr = lbr_saved;
+ if (curwin->w_p_lbr != lbr_saved) {
+ curwin->w_p_lbr = lbr_saved;
+ get_op_vcol(oap, redo_VIsual_mode, false);
+ }
+
op_replace(oap, cap->nchar);
}
break;
@@ -2027,6 +1989,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
deleteFold(oap->start.lnum, oap->end.lnum,
oap->op_type == OP_FOLDDELREC, oap->is_VIsual);
break;
+
+ case OP_NR_ADD:
+ case OP_NR_SUB:
+ if (empty_region_error) {
+ vim_beep(BO_OPER);
+ CancelRedo();
+ } else {
+ VIsual_active = true;
+ curwin->w_p_lbr = lbr_saved;
+ op_addsub(oap, cap->count1, redo_VIsual_arg);
+ VIsual_active = false;
+ }
+ check_cursor_col();
+ break;
default:
clearopbeep(oap);
}
@@ -2035,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;
@@ -2044,7 +2020,6 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
} else {
curwin->w_cursor = old_cursor;
}
- oap->block_mode = false;
clearop(oap);
}
curwin->w_p_lbr = lbr_saved;
@@ -2111,16 +2086,18 @@ 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->block_mode)
+ 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
+ } else {
argv[0] = (char_u *)"char";
+ }
/* Reset virtual_op so that 'virtualedit' can be changed in the
* function. */
@@ -2368,8 +2345,12 @@ do_mouse (
if (mouse_row == 0 && firstwin->w_winrow > 0) {
if (is_drag) {
if (in_tab_line) {
- c1 = TabPageIdxs[mouse_col];
- tabpage_move(c1 <= 0 ? 9999 : c1 - 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;
}
@@ -2379,41 +2360,114 @@ do_mouse (
&& cmdwin_type == 0
&& mouse_col < Columns) {
in_tab_line = true;
- c1 = TabPageIdxs[mouse_col];
- if (c1 >= 0) {
- if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
- /* double click opens new page */
- end_visual_mode();
- tabpage_new();
- tabpage_move(c1 == 0 ? 9999 : c1 - 1);
- } else {
- /* Go to specified tab page, or next one if not clicking
- * on a label. */
- goto_tabpage(c1);
-
- /* It's like clicking on the status line of a window. */
- if (curwin != old_curwin)
+ c1 = tab_page_click_defs[mouse_col].tabnr;
+ switch (tab_page_click_defs[mouse_col].type) {
+ case kStlClickDisabled: {
+ break;
+ }
+ case kStlClickTabClose: {
+ tabpage_T *tp;
+
+ // Close the current or specified tab page.
+ if (c1 == 999) {
+ tp = curtab;
+ } else {
+ tp = find_tabpage(c1);
+ }
+ if (tp == curtab) {
+ if (first_tabpage->tp_next != NULL) {
+ tabpage_close(false);
+ }
+ } else if (tp != NULL) {
+ tabpage_close_other(tp, false);
+ }
+ break;
+ }
+ case kStlClickTabSwitch: {
+ if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
+ // double click opens new page
end_visual_mode();
+ tabpage_new();
+ tabpage_move(c1 == 0 ? 9999 : c1 - 1);
+ } else {
+ // Go to specified tab page, or next one if not clicking
+ // on a label.
+ goto_tabpage(c1);
+
+ // It's like clicking on the status line of a window.
+ if (curwin != old_curwin) {
+ end_visual_mode();
+ }
+ }
+ break;
+ }
+ case kStlClickFuncRun: {
+ typval_T argv[] = {
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_NUMBER,
+ .vval = {
+ .v_number = (varnumber_T) tab_page_click_defs[mouse_col].tabnr
+ },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_NUMBER,
+ .vval = {
+ .v_number = (((mod_mask & MOD_MASK_MULTI_CLICK)
+ == MOD_MASK_4CLICK)
+ ? 4
+ : ((mod_mask & MOD_MASK_MULTI_CLICK)
+ == MOD_MASK_3CLICK)
+ ? 3
+ : ((mod_mask & MOD_MASK_MULTI_CLICK)
+ == MOD_MASK_2CLICK)
+ ? 2
+ : 1)
+ },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_STRING,
+ .vval = { .v_string = (char_u *) (which_button == MOUSE_LEFT
+ ? "l"
+ : which_button == MOUSE_RIGHT
+ ? "r"
+ : which_button == MOUSE_MIDDLE
+ ? "m"
+ : "?") },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_STRING,
+ .vval = {
+ .v_string = (char_u[]) {
+ (char_u) (mod_mask & MOD_MASK_SHIFT ? 's' : ' '),
+ (char_u) (mod_mask & MOD_MASK_CTRL ? 'c' : ' '),
+ (char_u) (mod_mask & MOD_MASK_ALT ? 'a' : ' '),
+ (char_u) (mod_mask & MOD_MASK_META ? 'm' : ' '),
+ NUL
+ }
+ },
+ }
+ };
+ typval_T rettv;
+ int doesrange;
+ (void) call_func((char_u *) tab_page_click_defs[mouse_col].func,
+ (int) strlen(tab_page_click_defs[mouse_col].func),
+ &rettv, ARRAY_SIZE(argv), argv,
+ curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+ &doesrange, true, NULL);
+ clear_tv(&rettv);
+ break;
}
- } else if (c1 < 0) {
- tabpage_T *tp;
-
- /* Close the current or specified tab page. */
- if (c1 == -999)
- tp = curtab;
- else
- tp = find_tabpage(-c1);
- if (tp == curtab) {
- if (first_tabpage->tp_next != NULL)
- tabpage_close(false);
- } else if (tp != NULL)
- tabpage_close_other(tp, false);
}
}
return true;
} else if (is_drag && in_tab_line) {
- c1 = TabPageIdxs[mouse_col];
- tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
+ tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose
+ ? 9999
+ : tab_page_click_defs[mouse_col].tabnr - 1);
in_tab_line = false;
return false;
}
@@ -2477,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. */
@@ -2545,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 */
@@ -2717,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++;
+ }
}
}
}
@@ -2886,10 +2941,7 @@ void end_visual_mode(void)
if (!virtual_active())
curwin->w_cursor.coladd = 0;
- if (mode_displayed)
- clear_cmdline = true; /* unshow visual mode later */
- else
- clear_showcmd();
+ may_clear_cmdline();
adjust_cursor_eol();
}
@@ -3155,10 +3207,19 @@ static void unshift_special(cmdarg_T *cap)
cap->cmdchar = simplify_key(cap->cmdchar, &mod_mask);
}
-/*
- * Routines for displaying a partly typed command
- */
+/// If the mode is currently displayed clear the command line or update the
+/// command displayed.
+static void may_clear_cmdline(void)
+{
+ if (mode_displayed) {
+ // unshow visual mode later
+ clear_cmdline = true;
+ } else {
+ clear_showcmd();
+ }
+}
+// Routines for displaying a partly typed command
# define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30
static char_u showcmd_buf[SHOWCMD_BUFLEN];
static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; /* For push_showcmd() */
@@ -3185,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) {
@@ -3537,9 +3598,16 @@ static void nv_help(cmdarg_T *cap)
*/
static void nv_addsub(cmdarg_T *cap)
{
- if (!checkclearopq(cap->oap)
- && do_addsub(cap->cmdchar, cap->count1))
+ if (!VIsual_active && cap->oap->op_type == OP_NOP) {
prep_redo_cmd(cap);
+ cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB;
+ op_addsub(cap->oap, cap->count1, cap->arg);
+ cap->oap->op_type = OP_NOP;
+ } else if (VIsual_active) {
+ nv_operator(cap);
+ } else {
+ clearop(cap->oap);
+ }
}
/*
@@ -3716,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();
@@ -3861,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;
@@ -3987,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":
@@ -4033,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 */
@@ -4041,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 */
@@ -4061,6 +4132,7 @@ dozet:
case 'b': scroll_cursor_bot(0, true);
redraw_later(VALID);
+ set_fraction(curwin);
break;
/* "zH" - scroll screen right half-page */
@@ -4394,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)" */
@@ -4794,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') {
@@ -4874,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');
@@ -4961,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) {
@@ -5023,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);
+ }
}
}
@@ -5041,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);
+ }
}
}
}
@@ -5083,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);
@@ -5117,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.
@@ -5192,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();
@@ -5230,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
@@ -5265,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. */
@@ -5555,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! */
@@ -5573,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);
@@ -5599,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;
@@ -5633,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;
@@ -5737,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');
@@ -6011,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;
}
@@ -6361,9 +6435,20 @@ static void nv_g_cmd(cmdarg_T *cap)
bool flag = false;
switch (cap->nchar) {
- /*
- * "gR": Enter virtual replace mode.
- */
+ // "g^A/g^X": Sequentially increment visually selected region.
+ case Ctrl_A:
+ case Ctrl_X:
+ if (VIsual_active) {
+ cap->arg = true;
+ cap->cmdchar = cap->nchar;
+ cap->nchar = NUL;
+ nv_addsub(cap);
+ } else {
+ clearopbeep(oap);
+ }
+ break;
+
+ // "gR": Enter virtual replace mode.
case 'R':
cap->arg = true;
nv_Replace(cap);
@@ -6480,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);
@@ -6495,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);
@@ -6522,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
@@ -6555,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,
@@ -6583,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
@@ -6645,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;
@@ -6878,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);
}
}
@@ -6971,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);
@@ -7000,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);
+ }
}
/*
@@ -7034,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) {
@@ -7053,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)
@@ -7102,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);
@@ -7153,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)
@@ -7232,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 */
@@ -7466,6 +7554,13 @@ static void nv_object(cmdarg_T *cap)
flag = current_block(cap->oap, cap->count1, include, '<', '>');
break;
case 't': /* "at" = a tag block (xml and html) */
+ // Do not adjust oap->end in do_pending_operator()
+ // otherwise there are different results for 'dit'
+ // (note leading whitespace in last line):
+ // 1) <b> 2) <b>
+ // foobar foobar
+ // </b> </b>
+ cap->retval |= CA_NO_ADJ_OP_END;
flag = current_tagblock(cap->oap, cap->count1, include);
break;
case 'p': /* "ap" = a paragraph */
@@ -7658,6 +7753,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
@@ -7692,6 +7791,75 @@ static void nv_open(cmdarg_T *cap)
n_opencmd(cap);
}
+// Calculate start/end virtual columns for operating in block mode.
+static void get_op_vcol(
+ oparg_T *oap,
+ colnr_T redo_VIsual_vcol,
+ bool initial // when true: adjust position for 'selectmode'
+)
+{
+ colnr_T start;
+ colnr_T end;
+
+ if (VIsual_mode != Ctrl_V
+ || (!initial && oap->end.col < curwin->w_width)) {
+ return;
+ }
+
+ oap->motion_type = kMTBlockWise;
+
+ // prevent from moving onto a trail byte
+ if (has_mbyte) {
+ mb_adjustpos(curwin->w_buffer, &oap->end);
+ }
+
+ getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol);
+ if (!redo_VIsual_busy) {
+ getvvcol(curwin, &(oap->end), &start, NULL, &end);
+
+ if (start < oap->start_vcol) {
+ oap->start_vcol = start;
+ }
+ if (end > oap->end_vcol) {
+ if (initial && *p_sel == 'e'
+ && start >= 1
+ && start - 1 >= oap->end_vcol) {
+ oap->end_vcol = start - 1;
+ } else {
+ oap->end_vcol = end;
+ }
+ }
+ }
+
+ // if '$' was used, get oap->end_vcol from longest line
+ if (curwin->w_curswant == MAXCOL) {
+ curwin->w_cursor.col = MAXCOL;
+ oap->end_vcol = 0;
+ for (curwin->w_cursor.lnum = oap->start.lnum;
+ curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) {
+ getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
+ if (end > oap->end_vcol) {
+ oap->end_vcol = end;
+ }
+ }
+ } else if (redo_VIsual_busy) {
+ oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1;
+ }
+
+ // Correct oap->end.col and oap->start.col to be the
+ // upper-left and lower-right corner of the block area.
+ //
+ // (Actually, this does convert column positions into character
+ // positions)
+ curwin->w_cursor.lnum = oap->end.lnum;
+ coladvance(oap->end_vcol);
+ oap->end = curwin->w_cursor;
+
+ curwin->w_cursor = oap->start;
+ coladvance(oap->start_vcol);
+ oap->start = curwin->w_cursor;
+}
+
// Handle an arbitrary event in normal mode
static void nv_event(cmdarg_T *cap)
{
diff --git a/src/nvim/normal.h b/src/nvim/normal.h
index 01259de6cd..51170105ed 100644
--- a/src/nvim/normal.h
+++ b/src/nvim/normal.h
@@ -10,32 +10,42 @@
#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 */
- 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 */
- bool end_adjusted; /* backuped b_op_end one char (only used by
- do_format()) */
- pos_T start; /* start of the operator */
- pos_T end; /* end of the operator */
- pos_T cursor_start; /* cursor position before motion for "gw" */
+ int op_type; // current pending operator type
+ int regname; // register to use for the operator
+ 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 kMTCharWise)
+ bool end_adjusted; // backuped b_op_end one char (only used by
+ // do_format())
+ pos_T start; // start of the operator
+ pos_T end; // end of the operator
+ pos_T cursor_start; // cursor position before motion for "gw"
- long line_count; /* number of lines from op_start to op_end
- (inclusive) */
- bool empty; /* op_start and op_end the same (only used by
- op_change()) */
- bool is_VIsual; /* operator on Visual area */
- bool block_mode; /* current operator is Visual block mode */
- colnr_T start_vcol; /* start col for block mode operator */
- colnr_T end_vcol; /* end col for block mode operator */
+ long line_count; // number of lines from op_start to op_end
+ // (inclusive)
+ bool empty; // op_start and op_end the same (only used by
+ // op_change())
+ bool is_VIsual; // operator on Visual area
+ colnr_T start_vcol; // start col for block mode operator
+ colnr_T end_vcol; // end col for block mode operator
long prev_opcount; // ca.opcount saved for K_EVENT
long prev_count0; // ca.count0 saved for K_EVENT
} oparg_T;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 956b9c7758..eda963ff77 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"
@@ -87,34 +88,36 @@ struct block_def {
*/
static char opchars[][3] =
{
- {NUL, NUL, FALSE}, /* OP_NOP */
- {'d', NUL, FALSE}, /* OP_DELETE */
- {'y', NUL, FALSE}, /* OP_YANK */
- {'c', NUL, FALSE}, /* OP_CHANGE */
- {'<', NUL, TRUE}, /* OP_LSHIFT */
- {'>', NUL, TRUE}, /* OP_RSHIFT */
- {'!', NUL, TRUE}, /* OP_FILTER */
- {'g', '~', FALSE}, /* OP_TILDE */
- {'=', NUL, TRUE}, /* OP_INDENT */
- {'g', 'q', TRUE}, /* OP_FORMAT */
- {':', NUL, TRUE}, /* OP_COLON */
- {'g', 'U', FALSE}, /* OP_UPPER */
- {'g', 'u', FALSE}, /* OP_LOWER */
- {'J', NUL, TRUE}, /* DO_JOIN */
- {'g', 'J', TRUE}, /* DO_JOIN_NS */
- {'g', '?', FALSE}, /* OP_ROT13 */
- {'r', NUL, FALSE}, /* OP_REPLACE */
- {'I', NUL, FALSE}, /* OP_INSERT */
- {'A', NUL, FALSE}, /* OP_APPEND */
- {'z', 'f', TRUE}, /* OP_FOLD */
- {'z', 'o', TRUE}, /* OP_FOLDOPEN */
- {'z', 'O', TRUE}, /* OP_FOLDOPENREC */
- {'z', 'c', TRUE}, /* OP_FOLDCLOSE */
- {'z', 'C', TRUE}, /* OP_FOLDCLOSEREC */
- {'z', 'd', TRUE}, /* OP_FOLDDEL */
- {'z', 'D', TRUE}, /* OP_FOLDDELREC */
- {'g', 'w', TRUE}, /* OP_FORMAT2 */
- {'g', '@', FALSE}, /* OP_FUNCTION */
+ { NUL, NUL, false }, // OP_NOP
+ { 'd', NUL, false }, // OP_DELETE
+ { 'y', NUL, false }, // OP_YANK
+ { 'c', NUL, false }, // OP_CHANGE
+ { '<', NUL, true }, // OP_LSHIFT
+ { '>', NUL, true }, // OP_RSHIFT
+ { '!', NUL, true }, // OP_FILTER
+ { 'g', '~', false }, // OP_TILDE
+ { '=', NUL, true }, // OP_INDENT
+ { 'g', 'q', true }, // OP_FORMAT
+ { ':', NUL, true }, // OP_COLON
+ { 'g', 'U', false }, // OP_UPPER
+ { 'g', 'u', false }, // OP_LOWER
+ { 'J', NUL, true }, // DO_JOIN
+ { 'g', 'J', true }, // DO_JOIN_NS
+ { 'g', '?', false }, // OP_ROT13
+ { 'r', NUL, false }, // OP_REPLACE
+ { 'I', NUL, false }, // OP_INSERT
+ { 'A', NUL, false }, // OP_APPEND
+ { 'z', 'f', true }, // OP_FOLD
+ { 'z', 'o', true }, // OP_FOLDOPEN
+ { 'z', 'O', true }, // OP_FOLDOPENREC
+ { 'z', 'c', true }, // OP_FOLDCLOSE
+ { 'z', 'C', true }, // OP_FOLDCLOSEREC
+ { 'z', 'd', true }, // OP_FOLDDEL
+ { 'z', 'D', true }, // OP_FOLDDELREC
+ { 'g', 'w', true }, // OP_FORMAT2
+ { 'g', '@', false }, // OP_FUNCTION
+ { Ctrl_A, NUL, false }, // OP_NR_ADD
+ { Ctrl_X, NUL, false }, // OP_NR_SUB
};
/*
@@ -125,13 +128,27 @@ int get_op_type(int char1, int char2)
{
int i;
- if (char1 == 'r') /* ignore second character */
+ if (char1 == 'r') {
+ // ignore second character
return OP_REPLACE;
- if (char1 == '~') /* when tilde is an operator */
+ }
+ if (char1 == '~') {
+ // when tilde is an operator
return OP_TILDE;
- for (i = 0;; i++)
- if (opchars[i][0] == char1 && opchars[i][1] == char2)
+ }
+ if (char1 == 'g' && char2 == Ctrl_A) {
+ // add
+ return OP_NR_ADD;
+ }
+ if (char1 == 'g' && char2 == Ctrl_X) {
+ // subtract
+ return OP_NR_SUB;
+ }
+ for (i = 0;; i++) {
+ if (opchars[i][0] == char1 && opchars[i][1] == char2) {
break;
+ }
+ }
return i;
}
@@ -174,20 +191,20 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
(linenr_T)(oap->end.lnum + 1)) == FAIL)
return;
- if (oap->block_mode)
+ if (oap->motion_type == kMTBlockWise) {
block_col = curwin->w_cursor.col;
+ }
for (i = oap->line_count - 1; i >= 0; i--) {
first_char = *get_cursor_line_ptr();
- if (first_char == NUL) /* empty line */
+ if (first_char == NUL) { // empty line
curwin->w_cursor.col = 0;
- else if (oap->block_mode)
+ } else if (oap->motion_type == kMTBlockWise) {
shift_block(oap, amount);
- else
- /* Move the line right if it doesn't start with '#', 'smartindent'
- * isn't set or 'cindent' isn't set or '#' isn't in 'cino'. */
- if (first_char != '#' || !preprocs_left()) {
- shift_line(oap->op_type == OP_LSHIFT, p_sr, amount, FALSE);
+ } else if (first_char != '#' || !preprocs_left()) {
+ // Move the line right if it doesn't start with '#', 'smartindent'
+ // isn't set or 'cindent' isn't set or '#' isn't in 'cino'.
+ shift_line(oap->op_type == OP_LSHIFT, p_sr, amount, false);
}
++curwin->w_cursor.lnum;
}
@@ -196,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->block_mode) {
+ 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 ">>" */
@@ -554,7 +571,7 @@ void op_reindent(oparg_T *oap, Indenter how)
{
long i;
char_u *l;
- int count;
+ int amount;
linenr_T first_changed = 0;
linenr_T last_changed = 0;
linenr_T start_lnum = curwin->w_cursor.lnum;
@@ -582,11 +599,11 @@ void op_reindent(oparg_T *oap, Indenter how)
|| how != get_lisp_indent) {
l = skipwhite(get_cursor_line_ptr());
if (*l == NUL) /* empty or blank line */
- count = 0;
+ amount = 0;
else
- count = how(); /* get the indent for this line */
+ amount = how(); /* get the indent for this line */
- if (set_indent(count, SIN_UNDO)) {
+ if (amount >= 0 && set_indent(amount, SIN_UNDO)) {
/* did change the indent, call changed_lines() later */
if (first_changed == 0)
first_changed = curwin->w_cursor.lnum;
@@ -703,17 +720,16 @@ char_u *get_expr_line_src(void)
/// @param writing allow only writable registers
bool valid_yank_reg(int regname, bool writing)
{
- if ( (regname > 0 && ASCII_ISALNUM(regname))
- || (!writing && vim_strchr((char_u *)
- "/.%#:="
- , regname) != NULL)
- || regname == '"'
- || regname == '-'
- || regname == '_'
- || regname == '*'
- || regname == '+'
- )
+ if ((regname > 0 && ASCII_ISALNUM(regname))
+ || (!writing && vim_strchr((char_u *) "/.%:=" , regname) != NULL)
+ || regname == '#'
+ || regname == '"'
+ || regname == '-'
+ || regname == '_'
+ || regname == '*'
+ || regname == '+') {
return true;
+ }
return false;
}
@@ -795,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;
}
/*
@@ -820,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;
@@ -902,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;
@@ -994,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;
@@ -1120,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');
+ }
}
}
}
@@ -1244,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;
@@ -1269,14 +1282,11 @@ 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);
+ }
/* Check for CTRL-C, in case someone tries to paste a few thousand
* lines and gets bored. */
@@ -1318,41 +1328,41 @@ 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
- && !oap->is_VIsual
- && !oap->block_mode
- && oap->line_count > 1
- && oap->motion_force == NUL
- && oap->op_type == OP_DELETE) {
+ if (oap->motion_type == kMTCharWise
+ && !oap->is_VIsual
+ && oap->line_count > 1
+ && oap->motion_force == NUL
+ && oap->op_type == OP_DELETE) {
ptr = ml_get(oap->end.lnum) + oap->end.col;
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 == MCHAR
- && oap->line_count == 1
- && oap->op_type == OP_DELETE
- && *ml_get(oap->start.lnum) == NUL) {
- /*
- * It's an error to operate on an empty region, when 'E' included in
- * 'cpoptions' (Vi compatible).
- */
- if (virtual_op)
- /* Virtual editing: Nothing gets deleted, but we set the '[ and ']
- * marks as if it happened. */
+ if (oap->motion_type != kMTLineWise
+ && oap->line_count == 1
+ && oap->op_type == OP_DELETE
+ && *ml_get(oap->start.lnum) == NUL) {
+ // It's an error to operate on an empty region, when 'E' included in
+ // 'cpoptions' (Vi compatible).
+ if (virtual_op) {
+ // Virtual editing: Nothing gets deleted, but we set the '[ and ']
+ // marks as if it happened.
goto setmarks;
- if (vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL)
+ }
+ if (vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL) {
beep_flush();
+ }
return OK;
}
@@ -1375,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--)
@@ -1388,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);
}
}
@@ -1403,10 +1414,11 @@ int op_delete(oparg_T *oap)
/*
* block mode delete
*/
- if (oap->block_mode) {
+ if (oap->motion_type == kMTBlockWise) {
if (u_save((linenr_T)(oap->start.lnum - 1),
- (linenr_T)(oap->end.lnum + 1)) == FAIL)
+ (linenr_T)(oap->end.lnum + 1)) == FAIL) {
return FAIL;
+ }
for (lnum = curwin->w_cursor.lnum; lnum <= oap->end.lnum; ++lnum) {
block_prep(oap, &bd, lnum, TRUE);
@@ -1440,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
@@ -1539,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->block_mode) {
+ if (oap->motion_type == kMTBlockWise) {
curbuf->b_op_end.lnum = oap->end.lnum;
curbuf->b_op_end.col = oap->start.col;
} else
@@ -1655,7 +1643,7 @@ int op_replace(oparg_T *oap, int c)
/*
* block mode replace
*/
- if (oap->block_mode) {
+ 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 */
@@ -1744,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));
@@ -1837,8 +1823,8 @@ void op_tilde(oparg_T *oap)
return;
pos = oap->start;
- if (oap->block_mode) { /* 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);
@@ -1849,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));
@@ -1991,6 +1977,7 @@ void op_insert(oparg_T *oap, long count1)
char_u *firstline, *ins_text;
struct block_def bd;
int i;
+ pos_T t1;
/* edit() changes this - record it for OP_APPEND */
bd.is_MAX = (curwin->w_curswant == MAXCOL);
@@ -1999,11 +1986,11 @@ void op_insert(oparg_T *oap, long count1)
curwin->w_cursor.lnum = oap->start.lnum;
update_screen(INVERTED);
- if (oap->block_mode) {
- /* 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
- * coladvance_force(). */
+ 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
+ // coladvance_force().
if (curwin->w_cursor.coladd > 0) {
int old_ve_flags = ve_flags;
@@ -2025,7 +2012,7 @@ void op_insert(oparg_T *oap, long count1)
}
if (oap->op_type == OP_APPEND) {
- if (oap->block_mode
+ if (oap->motion_type == kMTBlockWise
&& curwin->w_cursor.coladd == 0
) {
/* Move the cursor to the character right of the block. */
@@ -2053,7 +2040,16 @@ void op_insert(oparg_T *oap, long count1)
}
}
- edit(NUL, FALSE, (linenr_T)count1);
+ t1 = oap->start;
+ edit(NUL, false, (linenr_T)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)) {
+ oap->start = curbuf->b_op_start_orig;
+ }
/* If user has moved off this line, we don't know what to do, so do
* nothing.
@@ -2061,7 +2057,7 @@ void op_insert(oparg_T *oap, long count1)
if (curwin->w_cursor.lnum != oap->start.lnum || got_int)
return;
- if (oap->block_mode) {
+ if (oap->motion_type == kMTBlockWise) {
struct block_def bd2;
/* The user may have moved the cursor before inserting something, try
@@ -2069,21 +2065,23 @@ void op_insert(oparg_T *oap, long count1)
if (oap->start.lnum == curbuf->b_op_start_orig.lnum && !bd.is_MAX) {
if (oap->op_type == OP_INSERT
&& oap->start.col + oap->start.coladd
- != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) {
+ != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) {
+ size_t t = getviscol2(curbuf->b_op_start_orig.col,
+ curbuf->b_op_start_orig.coladd);
oap->start.col = curbuf->b_op_start_orig.col;
- pre_textlen -= getviscol2(oap->start.col, oap->start.coladd)
- - oap->start_vcol;
- oap->start_vcol = getviscol2(oap->start.col, oap->start.coladd);
+ pre_textlen -= t - oap->start_vcol;
+ oap->start_vcol = t;
} else if (oap->op_type == OP_APPEND
&& oap->end.col + oap->end.coladd
- >= curbuf->b_op_start_orig.col
- + curbuf->b_op_start_orig.coladd) {
+ >= curbuf->b_op_start_orig.col
+ + curbuf->b_op_start_orig.coladd) {
+ size_t t = getviscol2(curbuf->b_op_start_orig.col,
+ curbuf->b_op_start_orig.coladd);
oap->start.col = curbuf->b_op_start_orig.col;
/* reset pre_textlen to the value of OP_INSERT */
pre_textlen += bd.textlen;
- pre_textlen -= getviscol2(oap->start.col, oap->start.coladd)
- - oap->start_vcol;
- oap->start_vcol = getviscol2(oap->start.col, oap->start.coladd);
+ pre_textlen -= t - oap->start_vcol;
+ oap->start_vcol = t;
oap->op_type = OP_INSERT;
}
}
@@ -2146,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
@@ -2166,21 +2164,23 @@ int op_change(oparg_T *oap)
&& !virtual_op)
inc_cursor();
- /* check for still on same line (<CR> in inserted text meaningless) */
- /* skip blank lines too */
- if (oap->block_mode) {
- /* Add spaces before getting the current line length. */
+ // check for still on same line (<CR> in inserted text meaningless)
+ // skip blank lines too
+ if (oap->motion_type == kMTBlockWise) {
+ // Add spaces before getting the current line length.
if (virtual_op && (curwin->w_cursor.coladd > 0
- || gchar_cursor() == NUL))
+ || gchar_cursor() == NUL)) {
coladvance_force(getviscol());
+ }
firstline = ml_get(oap->start.lnum);
pre_textlen = (long)STRLEN(firstline);
pre_indent = (long)(skipwhite(firstline) - firstline);
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);
@@ -2189,9 +2189,10 @@ int op_change(oparg_T *oap)
* block.
* Don't repeat the insert when Insert mode ended with CTRL-C.
*/
- if (oap->block_mode && 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. */
+ 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.
firstline = ml_get(oap->start.lnum);
if (bd.textcol > (colnr_T)pre_indent) {
long new_indent = (long)(skipwhite(firstline) - firstline);
@@ -2303,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;
}
@@ -2314,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;
@@ -2332,20 +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
- && oap->start.col == 0
- && !oap->inclusive
- && (!oap->is_VIsual || *p_sel == 'o')
- && !oap->block_mode
- && oap->end.col == 0
- && yanklines > 1) {
- yanktype = MLINE;
- --yankendlnum;
- --yanklines;
+ if (oap->motion_type == kMTCharWise
+ && oap->start.col == 0
+ && !oap->inclusive
+ && (!oap->is_VIsual || *p_sel == 'o')
+ && oap->end.col == 0
+ && yanklines > 1) {
+ 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;
@@ -2354,9 +2356,8 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
y_idx = 0;
lnum = oap->start.lnum;
- if (oap->block_mode) {
- /* Visual block mode */
- reg->y_type = MBLOCK; /* set the yank register type */
+ if (yank_type == kMTBlockWise) {
+ // Visual block mode
reg->y_width = oap->end_vcol - oap->start_vcol;
if (curwin->w_curswant == MAXCOL && reg->y_width > 0)
@@ -2365,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;
@@ -2435,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);
}
}
@@ -2446,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]);
@@ -2470,25 +2476,26 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
if (curwin->w_p_rnu) {
redraw_later(SOME_VALID); // cursor moved to start
}
- if (message) { /* Display message about yank? */
- if (yanktype == MCHAR
- && !oap->block_mode
- && yanklines == 1)
+ if (message) { // Display message about yank?
+ if (yank_type == kMTCharWise && yanklines == 1) {
yanklines = 0;
- /* Some versions of Vi use ">=" here, some don't... */
+ }
+ // Some versions of Vi use ">=" here, some don't...
if (yanklines > p_report) {
- /* redisplay now, so message is not deleted */
+ // redisplay now, so message is not deleted
update_topline_redraw();
if (yanklines == 1) {
- if (oap->block_mode)
+ if (yank_type == kMTBlockWise) {
MSG(_("block of 1 line yanked"));
- else
+ } else {
MSG(_("1 line yanked"));
- } else if (oap->block_mode)
+ }
+ } else if (yank_type == kMTBlockWise) {
smsg(_("block of %" PRId64 " lines yanked"),
(int64_t)yanklines);
- else
+ } else {
smsg(_("%" PRId64 " lines yanked"), (int64_t)yanklines);
+ }
}
}
@@ -2497,9 +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
- && !oap->block_mode
- ) {
+ if (yank_type == kMTLineWise) {
curbuf->b_op_start.col = 0;
curbuf->b_op_end.col = MAXCOL;
}
@@ -2521,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.
@@ -2537,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;
@@ -2599,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.
@@ -2618,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;
}
}
@@ -2659,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) {
@@ -2683,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"),
@@ -2692,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. */
@@ -2722,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. */
@@ -2742,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;
@@ -2890,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());
@@ -2926,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) {
@@ -2961,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]);
@@ -2996,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) {
@@ -3028,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;
@@ -3059,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;
@@ -3124,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 */
@@ -3210,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();
}
@@ -3926,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 */
@@ -4170,285 +4234,513 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, int i
bdp->textstart = pstart;
}
-
-static void reverse_line(char_u *s)
+/// Handle the add/subtract operator.
+///
+/// @param[in] oap Arguments of operator.
+/// @param[in] Prenum1 Amount of addition or subtraction.
+/// @param[in] g_cmd Prefixed with `g`.
+void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
{
- int i, j;
- char_u c;
+ pos_T pos;
+ struct block_def bd;
+ ssize_t change_cnt = 0;
+ linenr_T amount = Prenum1;
- if ((i = (int)STRLEN(s) - 1) <= 0)
- return;
+ if (!VIsual_active) {
+ pos = curwin->w_cursor;
+ if (u_save_cursor() == FAIL) {
+ return;
+ }
+ change_cnt = do_addsub(oap->op_type, &pos, 0, amount);
+ if (change_cnt) {
+ changed_lines(pos.lnum, 0, pos.lnum + 1, 0L);
+ }
+ } else {
+ int one_change;
+ int length;
+ pos_T startpos;
+
+ if (u_save((linenr_T)(oap->start.lnum - 1),
+ (linenr_T)(oap->end.lnum + 1)) == FAIL) {
+ return;
+ }
+
+ pos = oap->start;
+ 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 == kMTLineWise) {
+ curwin->w_cursor.col = 0;
+ pos.col = 0;
+ length = (colnr_T)STRLEN(ml_get(pos.lnum));
+ } else {
+ // oap->motion_type == kMTCharWise
+ if (!oap->inclusive) {
+ dec(&(oap->end));
+ }
+ length = (colnr_T)STRLEN(ml_get(pos.lnum));
+ pos.col = 0;
+ if (pos.lnum == oap->start.lnum) {
+ pos.col += oap->start.col;
+ length -= oap->start.col;
+ }
+ if (pos.lnum == oap->end.lnum) {
+ length = (int)STRLEN(ml_get(oap->end.lnum));
+ if (oap->end.col >= length) {
+ oap->end.col = length - 1;
+ }
+ length = oap->end.col - pos.col + 1;
+ }
+ }
+ one_change = do_addsub(oap->op_type, &pos, length, amount);
+ if (one_change) {
+ // Remember the start position of the first change.
+ if (change_cnt == 0) {
+ startpos = curbuf->b_op_start;
+ }
+ change_cnt++;
+ }
- curwin->w_cursor.col = i - curwin->w_cursor.col;
- for (j = 0; j < i; j++, i--) {
- c = s[i]; s[i] = s[j]; s[j] = c;
+ if (g_cmd && one_change) {
+ amount += Prenum1;
+ }
+ }
+ if (change_cnt) {
+ changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L);
+ }
+
+ if (!change_cnt && oap->is_VIsual) {
+ // No change: need to remove the Visual selection
+ redraw_curbuf_later(INVERTED);
+ }
+
+ // Set '[ mark if something changed. Keep the last end
+ // position from do_addsub().
+ if (change_cnt > 0) {
+ curbuf->b_op_start = startpos;
+ }
+
+ if (change_cnt > p_report) {
+ if (change_cnt == 1) {
+ MSG(_("1 line changed"));
+ } else {
+ smsg((char *)_("%" PRId64 " lines changed"), (int64_t)change_cnt);
+ }
+ }
}
}
-# define RLADDSUBFIX(ptr) if (curwin->w_p_rl) reverse_line(ptr);
-
-/*
- * add or subtract 'Prenum1' from a number in a line
- * 'command' is CTRL-A for add, CTRL-X for subtract
- *
- * return FAIL for failure, OK otherwise
- */
-int do_addsub(int command, linenr_T Prenum1)
+/// Add or subtract from a number in a line.
+///
+/// @param op_type OP_NR_ADD or OP_NR_SUB.
+/// @param pos Cursor position.
+/// @param length Target number length.
+/// @param Prenum1 Amount of addition or subtraction.
+///
+/// @return true if some character was changed.
+int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
{
int col;
char_u *buf1;
char_u buf2[NUMBUFLEN];
- int hex; /* 'X' or 'x': hex; '0': octal */
- static int hexupper = FALSE; /* 0xABC */
- unsigned long n, oldn;
+ int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin
+ static bool hexupper = false; // 0xABC
+ unsigned long n;
+ unsigned long oldn;
char_u *ptr;
int c;
- int length = 0; /* character length of the number */
int todel;
- int dohex;
- int dooct;
- int doalp;
+ bool dohex;
+ bool dooct;
+ bool dobin;
+ bool doalp;
int firstdigit;
- int negative;
- int subtract;
+ bool subtract;
+ bool negative = false;
+ bool was_positive = true;
+ bool visual = VIsual_active;
+ bool did_change = false;
+ pos_T save_cursor = curwin->w_cursor;
+ int maxlen = 0;
+ pos_T startpos;
+ pos_T endpos;
+
+ dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); // "heX"
+ dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); // "Octal"
+ dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL); // "Bin"
+ doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); // "alPha"
+
+ curwin->w_cursor = *pos;
+ ptr = ml_get(pos->lnum);
+ col = pos->col;
+
+ if (*ptr == NUL) {
+ goto theend;
+ }
+
+ // First check if we are on a hexadecimal number, after the "0x".
+ if (!VIsual_active) {
+ if (dobin) {
+ while (col > 0 && ascii_isbdigit(ptr[col])) {
+ col--;
+ }
+ }
- dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); /* "heX" */
- dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); /* "Octal" */
- doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); /* "alPha" */
+ if (dohex) {
+ while (col > 0 && ascii_isxdigit(ptr[col])) {
+ col--;
+ }
+ }
+ if (dobin
+ && dohex
+ && !((col > 0
+ && (ptr[col] == 'X' || ptr[col] == 'x')
+ && ptr[col - 1] == '0'
+ && ascii_isxdigit(ptr[col + 1])))) {
+ // In case of binary/hexadecimal pattern overlap match, rescan
+
+ col = curwin->w_cursor.col;
+
+ while (col > 0 && ascii_isdigit(ptr[col])) {
+ col--;
+ }
+ }
- ptr = get_cursor_line_ptr();
- RLADDSUBFIX(ptr);
+ if ((dohex
+ && col > 0
+ && (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.
+ col--;
+ } else {
+ // Search forward and then backward to find the start of number.
+ col = pos->col;
- /*
- * First check if we are on a hexadecimal number, after the "0x".
- */
- col = curwin->w_cursor.col;
- if (dohex)
- while (col > 0 && ascii_isxdigit(ptr[col]))
- --col;
- if ( dohex
- && col > 0
- && (ptr[col] == 'X'
- || ptr[col] == 'x')
- && ptr[col - 1] == '0'
- && ascii_isxdigit(ptr[col + 1])) {
- /*
- * Found hexadecimal number, move to its start.
- */
- --col;
- } else {
- /*
- * Search forward and then backward to find the start of number.
- */
- col = curwin->w_cursor.col;
+ while (ptr[col] != NUL
+ && !ascii_isdigit(ptr[col])
+ && !(doalp && ASCII_ISALPHA(ptr[col]))) {
+ col++;
+ }
- while (ptr[col] != NUL
- && !ascii_isdigit(ptr[col])
- && !(doalp && ASCII_ISALPHA(ptr[col])))
- ++col;
+ while (col > 0
+ && ascii_isdigit(ptr[col - 1])
+ && !(doalp && ASCII_ISALPHA(ptr[col]))) {
+ col--;
+ }
+ }
+ }
- while (col > 0
- && ascii_isdigit(ptr[col - 1])
- && !(doalp && ASCII_ISALPHA(ptr[col])))
- --col;
+ if (visual) {
+ while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col])
+ && !(doalp && ASCII_ISALPHA(ptr[col]))) {
+ col++;
+ length--;
+ }
+
+ if (length == 0) {
+ goto theend;
+ }
+
+ if (col > pos->col && ptr[col - 1] == '-') {
+ negative = true;
+ was_positive = false;
+ }
}
- /*
- * If a number was found, and saving for undo works, replace the number.
- */
+ // If a number was found, and saving for undo works, replace the number.
firstdigit = ptr[col];
- RLADDSUBFIX(ptr);
- if ((!ascii_isdigit(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit)))
- || u_save_cursor() != OK) {
+ if (!ascii_isdigit(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit))) {
beep_flush();
- return FAIL;
+ goto theend;
}
- /* get ptr again, because u_save() may have changed it */
- ptr = get_cursor_line_ptr();
- RLADDSUBFIX(ptr);
-
if (doalp && ASCII_ISALPHA(firstdigit)) {
- /* decrement or increment alphabetic character */
- if (command == Ctrl_X) {
+ // decrement or increment alphabetic character
+ if (op_type == OP_NR_SUB) {
if (CharOrd(firstdigit) < Prenum1) {
- if (isupper(firstdigit))
+ if (isupper(firstdigit)) {
firstdigit = 'A';
- else
+ } else {
firstdigit = 'a';
- } else
+ }
+ } else {
firstdigit -= Prenum1;
+ }
} else {
if (26 - CharOrd(firstdigit) - 1 < Prenum1) {
- if (isupper(firstdigit))
+ if (isupper(firstdigit)) {
firstdigit = 'Z';
- else
+ } else {
firstdigit = 'z';
- } else
+ }
+ } else {
firstdigit += Prenum1;
+ }
}
curwin->w_cursor.col = col;
- (void)del_char(FALSE);
+ if (!did_change) {
+ startpos = curwin->w_cursor;
+ }
+ did_change = true;
+ (void)del_char(false);
ins_char(firstdigit);
+ endpos = curwin->w_cursor;
+ curwin->w_cursor.col = col;
} else {
- negative = FALSE;
- if (col > 0 && ptr[col - 1] == '-') { /* negative number */
- --col;
- negative = TRUE;
+ if (col > 0 && ptr[col - 1] == '-' && !visual) {
+ // negative number
+ col--;
+ negative = true;
}
- /* get the number value (unsigned) */
- vim_str2nr(ptr + col, &hex, &length, dooct, dohex, NULL, &n);
+ // get the number value (unsigned)
+ if (visual && VIsual_mode != 'V') {
+ maxlen = (curbuf->b_visual.vi_curswant == MAXCOL
+ ? (int)STRLEN(ptr) - col
+ : length);
+ }
- /* ignore leading '-' for hex and octal numbers */
- if (hex && negative) {
- ++col;
- --length;
- negative = FALSE;
+ vim_str2nr(ptr + col, &pre, &length,
+ 0 + (dobin ? STR2NR_BIN : 0)
+ + (dooct ? STR2NR_OCT : 0)
+ + (dohex ? STR2NR_HEX : 0),
+ NULL, &n, maxlen);
+
+ // ignore leading '-' for hex, octal and bin numbers
+ if (pre && negative) {
+ col++;
+ length--;
+ negative = false;
}
- /* add or subtract */
- subtract = FALSE;
- if (command == Ctrl_X)
- subtract ^= TRUE;
- if (negative)
- subtract ^= TRUE;
+ // add or subtract
+ subtract = false;
+ if (op_type == OP_NR_SUB) {
+ subtract ^= true;
+ }
+ if (negative) {
+ subtract ^= true;
+ }
oldn = n;
- if (subtract)
- n -= (unsigned long)Prenum1;
- else
- n += (unsigned long)Prenum1;
- /* handle wraparound for decimal numbers */
- if (!hex) {
+ n = subtract ? n - (unsigned long) Prenum1
+ : n + (unsigned long) Prenum1;
+
+ // handle wraparound for decimal numbers
+ if (!pre) {
if (subtract) {
if (n > oldn) {
n = 1 + (n ^ (unsigned long)-1);
- negative ^= TRUE;
+ negative ^= true;
}
- } else { /* add */
+ } else {
+ // add
if (n < oldn) {
n = (n ^ (unsigned long)-1);
- negative ^= TRUE;
+ negative ^= true;
}
}
- if (n == 0)
- negative = FALSE;
+ if (n == 0) {
+ negative = false;
+ }
}
- /*
- * Delete the old number.
- */
+ if (visual && !was_positive && !negative && col > 0) {
+ // need to remove the '-'
+ col--;
+ length++;
+ }
+
+ // Delete the old number.
curwin->w_cursor.col = col;
+ if (!did_change) {
+ startpos = curwin->w_cursor;
+ }
+ did_change = true;
todel = length;
c = gchar_cursor();
- /*
- * Don't include the '-' in the length, only the length of the part
- * after it is kept the same.
- */
- if (c == '-')
- --length;
+
+ // Don't include the '-' in the length, only the length of the part
+ // after it is kept the same.
+ if (c == '-') {
+ length--;
+ }
while (todel-- > 0) {
if (c < 0x100 && isalpha(c)) {
- if (isupper(c))
- hexupper = TRUE;
- else
- hexupper = FALSE;
+ if (isupper(c)) {
+ hexupper = true;
+ } else {
+ hexupper = false;
+ }
}
- /* del_char() will mark line needing displaying */
- (void)del_char(FALSE);
+ // del_char() will mark line needing displaying
+ (void)del_char(false);
c = gchar_cursor();
}
- /*
- * Prepare the leading characters in buf1[].
- * When there are many leading zeros it could be very long. Allocate
- * a bit too much.
- */
+ // Prepare the leading characters in buf1[].
+ // When there are many leading zeros it could be very long.
+ // Allocate a bit too much.
buf1 = xmalloc(length + NUMBUFLEN);
+ if (buf1 == NULL) {
+ goto theend;
+ }
ptr = buf1;
- if (negative) {
+ if (negative && (!visual || (visual && was_positive))) {
*ptr++ = '-';
}
- if (hex) {
+ if (pre) {
*ptr++ = '0';
- --length;
+ length--;
}
- if (hex == 'x' || hex == 'X') {
- *ptr++ = hex;
- --length;
+ if (pre == 'b' || pre == 'B' || pre == 'x' || pre == 'X') {
+ *ptr++ = pre;
+ length--;
}
- /*
- * Put the number characters in buf2[].
- */
- if (hex == 0)
- sprintf((char *)buf2, "%" PRIu64, (uint64_t)n);
- else if (hex == '0')
- sprintf((char *)buf2, "%" PRIo64, (uint64_t)n);
- else if (hex && hexupper)
- sprintf((char *)buf2, "%" PRIX64, (uint64_t)n);
- else
- sprintf((char *)buf2, "%" PRIx64, (uint64_t)n);
+ // Put the number characters in buf2[].
+ if (pre == 'b' || pre == 'B') {
+ size_t bits = 0;
+ size_t i = 0;
+
+ // leading zeros
+ for (bits = 8 * sizeof(n); bits > 0; bits--) {
+ if ((n >> (bits - 1)) & 0x1) {
+ break;
+ }
+ }
+
+ while (bits > 0) {
+ buf2[i++] = ((n >> --bits) & 0x1) ? '1' : '0';
+ }
+
+ buf2[i] = '\0';
+
+ } else if (pre == 0) {
+ vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n);
+ } else if (pre == '0') {
+ vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIo64, (uint64_t)n);
+ } else if (pre && hexupper) {
+ vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIX64, (uint64_t)n);
+ } else {
+ vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIx64, (uint64_t)n);
+ }
length -= (int)STRLEN(buf2);
- /*
- * Adjust number of zeros to the new number of digits, so the
- * total length of the number remains the same.
- * Don't do this when
- * the result may look like an octal number.
- */
- if (firstdigit == '0' && !(dooct && hex == 0))
- while (length-- > 0)
+ // Adjust number of zeros to the new number of digits, so the
+ // total length of the number remains the same.
+ // Don't do this when
+ // the result may look like an octal number.
+ if (firstdigit == '0' && !(dooct && pre == 0)) {
+ while (length-- > 0) {
*ptr++ = '0';
+ }
+ }
*ptr = NUL;
STRCAT(buf1, buf2);
- ins_str(buf1); /* insert the new number */
+ ins_str(buf1); // insert the new number
xfree(buf1);
+ endpos = curwin->w_cursor;
+ if (did_change && curwin->w_cursor.col) {
+ curwin->w_cursor.col--;
+ }
}
- --curwin->w_cursor.col;
- curwin->w_set_curswant = TRUE;
- ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
- RLADDSUBFIX(ptr);
- return OK;
+
+ if (did_change) {
+ // set the '[ and '] marks
+ curbuf->b_op_start = startpos;
+ curbuf->b_op_end = endpos;
+ if (curbuf->b_op_end.col > 0) {
+ curbuf->b_op_end.col--;
+ }
+ }
+
+theend:
+ if (visual) {
+ curwin->w_cursor = save_cursor;
+ }
+
+ return did_change;
}
/*
* 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`.
///
@@ -4527,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);
@@ -4545,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;
@@ -4589,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];
@@ -4639,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) {
@@ -4658,6 +4953,27 @@ void write_reg_contents_ex(int name,
return;
}
+ if (name == '#') {
+ buf_T *buf;
+
+ if (ascii_isdigit(*str)) {
+ int num = atoi((char *)str);
+
+ buf = buflist_findnr(num);
+ if (buf == NULL) {
+ EMSGN(_(e_nobufnr), (long)num);
+ }
+ } else {
+ buf = buflist_findnr(buflist_findpat(str, str + STRLEN(str),
+ true, false, false));
+ }
+ if (buf == NULL) {
+ return;
+ }
+ curwin->w_alt_fnum = buf->b_fnum;
+ return;
+ }
+
if (name == '=') {
size_t offset = 0;
size_t totlen = (size_t) len;
@@ -4698,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;
@@ -4729,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
}
@@ -4786,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;
@@ -4849,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;
@@ -4875,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;
@@ -4903,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.block_mode = true;
+ oparg.motion_type = kMTBlockWise;
oparg.op_type = OP_NOP;
getvcols(curwin, &min_pos, &max_pos,
&oparg.start_vcol, &oparg.end_vcol);
@@ -4964,7 +5281,7 @@ void cursor_pos_info(void)
&char_count_cursor, len, eol_size);
if (lnum == curbuf->b_ml.ml_line_count
&& !curbuf->b_p_eol
- && curbuf->b_p_bin
+ && (curbuf->b_p_bin || !curbuf->b_p_fixeol)
&& (long)STRLEN(s) < len)
byte_count_cursor -= eol_size;
}
@@ -4984,78 +5301,105 @@ 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)
+ // 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 (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);
+ }
+ }
+ }
+
+ // Don't shorten this message, the user asked for it.
+ bom_count = bomb_size();
+ if (bom_count > 0) {
+ vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
+ _("(+%" PRId64 " for BOM)"), (int64_t)byte_count);
+ }
+ if (dict == NULL) {
+ p = p_shm;
+ p_shm = (char_u *)"";
+ msg(IObuff);
+ p_shm = p;
+ }
+ }
+
+ if (dict != NULL) {
+ 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);
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);
+ dict_add_nr_str(dict, "visual_bytes", byte_count_cursor, NULL);
+ dict_add_nr_str(dict, "visual_chars", char_count_cursor, NULL);
+ dict_add_nr_str(dict, "visual_words", word_count_cursor, NULL);
} 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;
+ dict_add_nr_str(dict, "cursor_bytes", byte_count_cursor, NULL);
+ dict_add_nr_str(dict, "cursor_chars", char_count_cursor, NULL);
+ dict_add_nr_str(dict, "cursor_words", word_count_cursor, NULL);
+ }
}
}
@@ -5158,16 +5502,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;
@@ -5175,7 +5519,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 *));
@@ -5196,20 +5540,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]);
@@ -5258,17 +5602,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);
@@ -5309,7 +5655,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 507f933acf..8c8a586957 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -35,37 +35,39 @@ typedef int (*Indenter)(void);
#define PLUS_REGISTER 38
#define NUM_REGISTERS 39
-/*
- * Operator IDs; The order must correspond to opchars[] in ops.c!
- */
-#define OP_NOP 0 /* no pending operation */
-#define OP_DELETE 1 /* "d" delete operator */
-#define OP_YANK 2 /* "y" yank operator */
-#define OP_CHANGE 3 /* "c" change operator */
-#define OP_LSHIFT 4 /* "<" left shift operator */
-#define OP_RSHIFT 5 /* ">" right shift operator */
-#define OP_FILTER 6 /* "!" filter operator */
-#define OP_TILDE 7 /* "g~" switch case operator */
-#define OP_INDENT 8 /* "=" indent operator */
-#define OP_FORMAT 9 /* "gq" format operator */
-#define OP_COLON 10 /* ":" colon operator */
-#define OP_UPPER 11 /* "gU" make upper case operator */
-#define OP_LOWER 12 /* "gu" make lower case operator */
-#define OP_JOIN 13 /* "J" join operator, only for Visual mode */
-#define OP_JOIN_NS 14 /* "gJ" join operator, only for Visual mode */
-#define OP_ROT13 15 /* "g?" rot-13 encoding */
-#define OP_REPLACE 16 /* "r" replace chars, only for Visual mode */
-#define OP_INSERT 17 /* "I" Insert column, only for Visual mode */
-#define OP_APPEND 18 /* "A" Append column, only for Visual mode */
-#define OP_FOLD 19 /* "zf" define a fold */
-#define OP_FOLDOPEN 20 /* "zo" open folds */
-#define OP_FOLDOPENREC 21 /* "zO" open folds recursively */
-#define OP_FOLDCLOSE 22 /* "zc" close folds */
-#define OP_FOLDCLOSEREC 23 /* "zC" close folds recursively */
-#define OP_FOLDDEL 24 /* "zd" delete folds */
-#define OP_FOLDDELREC 25 /* "zD" delete folds recursively */
-#define OP_FORMAT2 26 /* "gw" format operator, keeps cursor pos */
-#define OP_FUNCTION 27 /* "g@" call 'operatorfunc' */
+// Operator IDs; The order must correspond to opchars[] in ops.c!
+#define OP_NOP 0 // no pending operation
+#define OP_DELETE 1 // "d" delete operator
+#define OP_YANK 2 // "y" yank operator
+#define OP_CHANGE 3 // "c" change operator
+#define OP_LSHIFT 4 // "<" left shift operator
+#define OP_RSHIFT 5 // ">" right shift operator
+#define OP_FILTER 6 // "!" filter operator
+#define OP_TILDE 7 // "g~" switch case operator
+#define OP_INDENT 8 // "=" indent operator
+#define OP_FORMAT 9 // "gq" format operator
+#define OP_COLON 10 // ":" colon operator
+#define OP_UPPER 11 // "gU" make upper case operator
+#define OP_LOWER 12 // "gu" make lower case operator
+#define OP_JOIN 13 // "J" join operator, only for Visual mode
+#define OP_JOIN_NS 14 // "gJ" join operator, only for Visual mode
+#define OP_ROT13 15 // "g?" rot-13 encoding
+#define OP_REPLACE 16 // "r" replace chars, only for Visual mode
+#define OP_INSERT 17 // "I" Insert column, only for Visual mode
+#define OP_APPEND 18 // "A" Append column, only for Visual mode
+#define OP_FOLD 19 // "zf" define a fold
+#define OP_FOLDOPEN 20 // "zo" open folds
+#define OP_FOLDOPENREC 21 // "zO" open folds recursively
+#define OP_FOLDCLOSE 22 // "zc" close folds
+#define OP_FOLDCLOSEREC 23 // "zC" close folds recursively
+#define OP_FOLDDEL 24 // "zd" delete folds
+#define OP_FOLDDELREC 25 // "zD" delete folds recursively
+#define OP_FORMAT2 26 // "gw" format operator, keeps cursor pos
+#define OP_FUNCTION 27 // "g@" call 'operatorfunc'
+#define OP_NR_ADD 28 // "<C-A>" Add to the number or alphabetic
+ // character (OP_ADD conflicts with Perl)
+#define OP_NR_SUB 29 // "<C-X>" Subtract from the number or
+ // alphabetic character
/// Flags for get_reg_contents().
enum GRegFlags {
@@ -76,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 dbcd230186..2f22c245dd 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"
@@ -122,6 +123,7 @@ static char_u *p_cpt;
static char_u *p_cfu;
static char_u *p_ofu;
static int p_eol;
+static int p_fixeol;
static int p_et;
static char_u *p_fenc;
static char_u *p_ff;
@@ -167,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 */
@@ -217,26 +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 ISK_LATIN1 (char_u *)"@,48-57,_,192-255"
-
-/* 'isprint' for latin1 is also used for MS-Windows cp1252, where 0x80 is used
- * for the currency sign. */
-# define ISP_LATIN1 (char_u *)"@,161-255"
+#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," \
@@ -263,30 +262,31 @@ typedef struct vimoption {
#define PARAM_COUNT ARRAY_SIZE(options)
-static char *(p_ambw_values[]) = {"single", "double", NULL};
-static char *(p_bg_values[]) = {"light", "dark", NULL};
-static char *(p_nf_values[]) = {"octal", "hex", "alpha", NULL};
-static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL};
-static char *(p_wop_values[]) = {"tagfile", NULL};
-static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
-static char *(p_mousem_values[]) =
-{"extend", "popup", "popup_setpos", "mac", NULL};
-static char *(p_sel_values[]) = {"inclusive", "exclusive", "old", NULL};
-static char *(p_slm_values[]) = {"mouse", "key", "cmd", NULL};
-static char *(p_km_values[]) = {"startsel", "stopsel", NULL};
-static char *(p_scbopt_values[]) = {"ver", "hor", "jump", NULL};
-static char *(p_debug_values[]) = {"msg", "throw", "beep", NULL};
-static char *(p_ead_values[]) = {"both", "ver", "hor", NULL};
-static char *(p_buftype_values[]) =
-{"nofile", "nowrite", "quickfix", "help", "acwrite", "terminal", NULL};
-static char *(p_bufhidden_values[]) = {"hide", "unload", "delete", "wipe", NULL};
-static char *(p_bs_values[]) = {"indent", "eol", "start", NULL};
-static char *(p_fdm_values[]) = {"manual", "expr", "marker", "indent", "syntax",
- "diff",
- NULL};
-static char *(p_fcl_values[]) = {"all", NULL};
-static char *(p_cot_values[]) = {"menu", "menuone", "longest", "preview",
- "noinsert", "noselect", NULL};
+static char *(p_ambw_values[]) = { "single", "double", NULL };
+static char *(p_bg_values[]) = { "light", "dark", NULL };
+static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", NULL };
+static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL };
+static char *(p_wop_values[]) = { "tagfile", NULL };
+static char *(p_wak_values[]) = { "yes", "menu", "no", NULL };
+static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos",
+ "mac", NULL };
+static char *(p_sel_values[]) = { "inclusive", "exclusive", "old", NULL };
+static char *(p_slm_values[]) = { "mouse", "key", "cmd", NULL };
+static char *(p_km_values[]) = { "startsel", "stopsel", NULL };
+static char *(p_scbopt_values[]) = { "ver", "hor", "jump", NULL };
+static char *(p_debug_values[]) = { "msg", "throw", "beep", NULL };
+static char *(p_ead_values[]) = { "both", "ver", "hor", NULL };
+static char *(p_buftype_values[]) = { "nofile", "nowrite", "quickfix",
+ "help", "acwrite", "terminal", NULL };
+
+static char *(p_bufhidden_values[]) = { "hide", "unload", "delete",
+ "wipe", NULL };
+static char *(p_bs_values[]) = { "indent", "eol", "start", NULL };
+static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent",
+ "syntax", "diff", NULL };
+static char *(p_fcl_values[]) = { "all", NULL };
+static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview",
+ "noinsert", "noselect", NULL };
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
@@ -712,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.
@@ -752,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).
@@ -769,65 +759,20 @@ 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. */
+ // enc_locale() will try to find the encoding of the current locale.
+ // This will be used when 'default' is used as encoding specifier
+ // in 'fileencodings'
char_u *p = enc_locale();
- if (p != NULL) {
- char_u *save_enc;
-
- /* Try setting 'encoding' and check if the value is valid.
- * If not, go back to the default "utf-8". */
- save_enc = p_enc;
- p_enc = (char_u *) p;
- if (STRCMP(p_enc, "gb18030") == 0) {
- /* We don't support "gb18030", but "cp936" is a good substitute
- * for practical purposes, thus use that. It's not an alias to
- * still support conversion between gb18030 and utf-8. */
- p_enc = vim_strsave((char_u *)"cp936");
- xfree(p);
- }
- if (mb_init() == NULL) {
- opt_idx = findoption((char_u *)"encoding");
- if (opt_idx >= 0) {
- options[opt_idx].def_val[VI_DEFAULT] = p_enc;
- options[opt_idx].flags |= P_DEF_ALLOCED;
- }
-
-#if defined(MSWIN) || defined(MACOS)
- if (STRCMP(p_enc, "latin1") == 0
- || enc_utf8
- ) {
- /* Adjust the default for 'isprint' and 'iskeyword' to match
- * latin1. */
- set_string_option_direct((char_u *)"isp", -1,
- ISP_LATIN1, OPT_FREE, SID_NONE);
- set_string_option_direct((char_u *)"isk", -1,
- ISK_LATIN1, OPT_FREE, SID_NONE);
- opt_idx = findoption((char_u *)"isp");
- if (opt_idx >= 0)
- options[opt_idx].def_val[VIM_DEFAULT] = ISP_LATIN1;
- opt_idx = findoption((char_u *)"isk");
- if (opt_idx >= 0)
- options[opt_idx].def_val[VIM_DEFAULT] = ISK_LATIN1;
- (void)init_chartab();
- }
-#endif
-
- } else {
- xfree(p_enc);
- // mb_init() failed; fallback to utf8 and try again.
- p_enc = save_enc;
- mb_init();
- }
- } else {
- // enc_locale() failed; initialize the default (utf8).
- mb_init();
+ if (p == NULL) {
+ // use utf-8 as 'default' if locale encoding can't be detected.
+ p = vim_strsave((char_u *)"utf-8");
}
+ fenc_default = p;
+
+ // Initialize multibyte (utf-8) handling
+ mb_init();
// Don't change &encoding when resetting to defaults with ":set all&".
opt_idx = findoption((char_u *)"encoding");
@@ -1018,12 +963,9 @@ void set_init_2(void)
*/
void set_init_3(void)
{
-#if defined(UNIX) || defined(WIN3264)
- /*
- * Set 'shellpipe' and 'shellredir', depending on the 'shell' option.
- * This is done after other initializations, where 'shell' might have been
- * set, but only if they have not been set before.
- */
+ // Set 'shellpipe' and 'shellredir', depending on the 'shell' option.
+ // This is done after other initializations, where 'shell' might have been
+ // set, but only if they have not been set before.
int idx_srr;
int do_srr;
int idx_sp;
@@ -1080,8 +1022,6 @@ void set_init_3(void)
}
xfree(p);
}
-#endif
-
set_title_defaults();
}
@@ -1198,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;
@@ -1235,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 */
@@ -1482,7 +1424,7 @@ do_set (
} else if (*arg == '-' || ascii_isdigit(*arg)) {
// Allow negative (for 'undolevels'), octal and
// hex numbers.
- vim_str2nr(arg, NULL, &i, true, true, &value, NULL);
+ vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0);
if (arg[i] != NUL && !ascii_iswhite(arg[i])) {
errmsg = e_invarg;
goto skip;
@@ -1506,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;
@@ -1723,6 +1665,13 @@ do_set (
&& *newval != NUL);
if (adding) {
i = (int)STRLEN(origval);
+ // Strip a trailing comma, would get 2.
+ if (comma && i > 1
+ && (flags & P_ONECOMMA) == P_ONECOMMA
+ && origval[i - 1] == ','
+ && origval[i - 2] != '\\') {
+ i--;
+ }
memmove(newval + i + comma, newval,
STRLEN(newval) + 1);
memmove(newval, origval, (size_t)i);
@@ -1776,7 +1725,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
@@ -1791,11 +1740,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,
@@ -2109,13 +2057,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();
}
/*
@@ -2174,6 +2145,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);
@@ -2353,7 +2325,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 */
@@ -2369,7 +2341,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,
@@ -2378,10 +2350,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,
@@ -2894,22 +2866,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)) {
@@ -3002,13 +2959,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);
@@ -3028,6 +2989,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;
@@ -3465,6 +3444,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.
@@ -3592,6 +3595,9 @@ set_bool_option (
/* when 'endofline' is changed, redraw the window title */
else if ((int *)varp == &curbuf->b_p_eol) {
redraw_titles();
+ } else if ((int *)varp == &curbuf->b_p_fixeol) {
+ // when 'fixeol' is changed, redraw the window title
+ redraw_titles();
}
/* when 'bomb' is changed, redraw the window title and tab page text */
else if ((int *)varp == &curbuf->b_p_bomb) {
@@ -3817,7 +3823,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' */
@@ -3864,14 +3870,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);
@@ -4254,12 +4260,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);
@@ -4301,14 +4307,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;
/*
@@ -4332,25 +4340,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;
}
}
@@ -4418,6 +4432,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:
@@ -4670,40 +4693,35 @@ char_u *get_highlight_default(void)
return (char_u *)NULL;
}
-char_u *get_encoding_default(void)
-{
- int i;
-
- i = findoption((char_u *)"enc");
- if (i >= 0)
- return options[i].def_val[VI_DEFAULT];
- return (char_u *)NULL;
-}
-
/*
* 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
@@ -4764,9 +4782,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;
+ }
}
}
@@ -4955,18 +4974,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)
@@ -5139,6 +5155,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;
@@ -5192,6 +5212,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);
@@ -5229,6 +5250,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
@@ -5304,6 +5327,7 @@ static char_u *get_varp(vimoption_T *p)
case PV_CFU: return (char_u *)&(curbuf->b_p_cfu);
case PV_OFU: return (char_u *)&(curbuf->b_p_ofu);
case PV_EOL: return (char_u *)&(curbuf->b_p_eol);
+ case PV_FIXEOL: return (char_u *)&(curbuf->b_p_fixeol);
case PV_ET: return (char_u *)&(curbuf->b_p_et);
case PV_FENC: return (char_u *)&(curbuf->b_p_fenc);
case PV_FF: return (char_u *)&(curbuf->b_p_ff);
@@ -5548,7 +5572,9 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_bin = p_bin;
buf->b_p_bomb = p_bomb;
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;
@@ -5605,6 +5631,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);
@@ -5971,13 +5999,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;
@@ -6187,16 +6219,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)));
}
/*
@@ -6206,6 +6236,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;
@@ -6222,40 +6253,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;
@@ -6271,20 +6306,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;
@@ -6483,6 +6522,7 @@ void save_file_ff(buf_T *buf)
* from when editing started (save_file_ff() called).
* Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was
* changed and 'binary' is not set.
+ * Also when 'endofline' was changed and 'fixeol' is not set.
* When "ignore_empty" is true don't consider a new, empty buffer to be
* changed.
*/
@@ -6497,9 +6537,9 @@ bool file_ff_differs(buf_T *buf, bool ignore_empty)
&& *ml_get_buf(buf, (linenr_T)1, FALSE) == NUL)
return FALSE;
if (buf->b_start_ffc != *buf->b_p_ff)
- return TRUE;
- if (buf->b_p_bin && buf->b_start_eol != buf->b_p_eol)
- return TRUE;
+ return true;
+ if ((buf->b_p_bin || !buf->b_p_fixeol) && buf->b_start_eol != buf->b_p_eol)
+ return true;
if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb)
return TRUE;
if (buf->b_start_fenc == NULL)
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index d4d3410d5c..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 */
@@ -209,44 +226,58 @@
#define COM_ALL "nbsmexflrO" /* all flags for 'comments' option */
#define COM_MAX_LEN 50 /* maximum length of a part */
-/* flags for 'statusline' option */
-#define STL_FILEPATH 'f' /* path of file in buffer */
-#define STL_FULLPATH 'F' /* full path of file in buffer */
-#define STL_FILENAME 't' /* last part (tail) of file path */
-#define STL_COLUMN 'c' /* column og cursor*/
-#define STL_VIRTCOL 'v' /* virtual column */
-#define STL_VIRTCOL_ALT 'V' /* - with 'if different' display */
-#define STL_LINE 'l' /* line number of cursor */
-#define STL_NUMLINES 'L' /* number of lines in buffer */
-#define STL_BUFNO 'n' /* current buffer number */
-#define STL_KEYMAP 'k' /* 'keymap' when active */
-#define STL_OFFSET 'o' /* offset of character under cursor*/
-#define STL_OFFSET_X 'O' /* - in hexadecimal */
-#define STL_BYTEVAL 'b' /* byte value of character */
-#define STL_BYTEVAL_X 'B' /* - in hexadecimal */
-#define STL_ROFLAG 'r' /* readonly flag */
-#define STL_ROFLAG_ALT 'R' /* - other display */
-#define STL_HELPFLAG 'h' /* window is showing a help file */
-#define STL_HELPFLAG_ALT 'H' /* - other display */
-#define STL_FILETYPE 'y' /* 'filetype' */
-#define STL_FILETYPE_ALT 'Y' /* - other display */
-#define STL_PREVIEWFLAG 'w' /* window is showing the preview buf */
-#define STL_PREVIEWFLAG_ALT 'W' /* - other display */
-#define STL_MODIFIED 'm' /* modified flag */
-#define STL_MODIFIED_ALT 'M' /* - other display */
-#define STL_QUICKFIX 'q' /* quickfix window description */
-#define STL_PERCENTAGE 'p' /* percentage through file */
-#define STL_ALTPERCENT 'P' /* percentage as TOP BOT ALL or NN% */
-#define STL_ARGLISTSTAT 'a' /* argument list status as (x of y) */
-#define STL_PAGENUM 'N' /* page number (when printing)*/
-#define STL_VIM_EXPR '{' /* start of expression to substitute */
-#define STL_MIDDLEMARK '=' /* separation between left and right */
-#define STL_TRUNCMARK '<' /* truncation mark if line is too long*/
-#define STL_USER_HL '*' /* highlight from (User)1..9 or 0 */
-#define STL_HIGHLIGHT '#' /* highlight name */
-#define STL_TABPAGENR 'T' /* tab page label nr */
-#define STL_TABCLOSENR 'X' /* tab page close nr */
-#define STL_ALL ((char_u *) "fFtcvVlLknoObBrRhHmYyWwMqpPaN{#")
+/// 'statusline' option flags
+enum {
+ STL_FILEPATH = 'f', ///< Path of file in buffer.
+ STL_FULLPATH = 'F', ///< Full path of file in buffer.
+ STL_FILENAME = 't', ///< Last part (tail) of file path.
+ STL_COLUMN = 'c', ///< Column og cursor.
+ STL_VIRTCOL = 'v', ///< Virtual column.
+ STL_VIRTCOL_ALT = 'V', ///< - with 'if different' display.
+ STL_LINE = 'l', ///< Line number of cursor.
+ STL_NUMLINES = 'L', ///< Number of lines in buffer.
+ STL_BUFNO = 'n', ///< Current buffer number.
+ STL_KEYMAP = 'k', ///< 'keymap' when active.
+ STL_OFFSET = 'o', ///< Offset of character under cursor.
+ STL_OFFSET_X = 'O', ///< - in hexadecimal.
+ STL_BYTEVAL = 'b', ///< Byte value of character.
+ STL_BYTEVAL_X = 'B', ///< - in hexadecimal.
+ STL_ROFLAG = 'r', ///< Readonly flag.
+ STL_ROFLAG_ALT = 'R', ///< - other display.
+ STL_HELPFLAG = 'h', ///< Window is showing a help file.
+ STL_HELPFLAG_ALT = 'H', ///< - other display.
+ STL_FILETYPE = 'y', ///< 'filetype'.
+ STL_FILETYPE_ALT = 'Y', ///< - other display.
+ STL_PREVIEWFLAG = 'w', ///< Window is showing the preview buf.
+ STL_PREVIEWFLAG_ALT = 'W', ///< - other display.
+ STL_MODIFIED = 'm', ///< Modified flag.
+ STL_MODIFIED_ALT = 'M', ///< - other display.
+ STL_QUICKFIX = 'q', ///< Quickfix window description.
+ STL_PERCENTAGE = 'p', ///< Percentage through file.
+ STL_ALTPERCENT = 'P', ///< Percentage as TOP BOT ALL or NN%.
+ STL_ARGLISTSTAT = 'a', ///< Argument list status as (x of y).
+ STL_PAGENUM = 'N', ///< Page number (when printing).
+ STL_VIM_EXPR = '{', ///< Start of expression to substitute.
+ STL_MIDDLEMARK = '=', ///< Separation between left and right.
+ STL_TRUNCMARK = '<', ///< Truncation mark if line is too long.
+ STL_USER_HL = '*', ///< Highlight from (User)1..9 or 0.
+ STL_HIGHLIGHT = '#', ///< Highlight name.
+ STL_TABPAGENR = 'T', ///< Tab page label nr.
+ STL_TABCLOSENR = 'X', ///< Tab page close nr.
+ STL_CLICK_FUNC = '@', ///< Click region start.
+};
+/// C string containing all 'statusline' option flags
+#define STL_ALL ((char_u[]) { \
+ STL_FILEPATH, STL_FULLPATH, STL_FILENAME, STL_COLUMN, STL_VIRTCOL, \
+ STL_VIRTCOL_ALT, STL_LINE, STL_NUMLINES, STL_BUFNO, STL_KEYMAP, STL_OFFSET, \
+ STL_OFFSET_X, STL_BYTEVAL, STL_BYTEVAL_X, STL_ROFLAG, STL_ROFLAG_ALT, \
+ STL_HELPFLAG, STL_HELPFLAG_ALT, STL_FILETYPE, STL_FILETYPE_ALT, \
+ STL_PREVIEWFLAG, STL_PREVIEWFLAG_ALT, STL_MODIFIED, STL_MODIFIED_ALT, \
+ STL_QUICKFIX, STL_PERCENTAGE, STL_ALTPERCENT, STL_ARGLISTSTAT, STL_PAGENUM, \
+ STL_VIM_EXPR, STL_MIDDLEMARK, STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, \
+ STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \
+ 0, \
+})
/* flags used for parsed 'wildmode' */
#define WIM_FULL 1
@@ -405,27 +436,25 @@ static char *(p_fdo_values[]) = {"all", "block", "hor", "mark", "percent",
# define FDO_INSERT 0x100
# define FDO_UNDO 0x200
# define FDO_JUMP 0x400
-EXTERN char_u *p_fp; /* 'formatprg' */
-#ifdef HAVE_FSYNC
-EXTERN int p_fs; /* 'fsync' */
-#endif
-EXTERN int p_gd; /* 'gdefault' */
-EXTERN char_u *p_pdev; /* 'printdevice' */
-EXTERN char_u *p_penc; /* 'printencoding' */
-EXTERN char_u *p_pexpr; /* 'printexpr' */
-EXTERN char_u *p_pmfn; /* 'printmbfont' */
-EXTERN char_u *p_pmcs; /* 'printmbcharset' */
-EXTERN char_u *p_pfn; /* 'printfont' */
-EXTERN char_u *p_popt; /* 'printoptions' */
-EXTERN char_u *p_header; /* 'printheader' */
-EXTERN int p_prompt; /* 'prompt' */
-EXTERN char_u *p_guicursor; /* 'guicursor' */
-EXTERN char_u *p_hf; /* 'helpfile' */
-EXTERN long p_hh; /* 'helpheight' */
-EXTERN char_u *p_hlg; /* 'helplang' */
-EXTERN int p_hid; /* 'hidden' */
-/* Use P_HID to check if a buffer is to be hidden when it is no longer
- * visible in a window. */
+EXTERN char_u *p_fp; // 'formatprg'
+EXTERN int p_fs; // 'fsync'
+EXTERN int p_gd; // 'gdefault'
+EXTERN char_u *p_pdev; // 'printdevice'
+EXTERN char_u *p_penc; // 'printencoding'
+EXTERN char_u *p_pexpr; // 'printexpr'
+EXTERN char_u *p_pmfn; // 'printmbfont'
+EXTERN char_u *p_pmcs; // 'printmbcharset'
+EXTERN char_u *p_pfn; // 'printfont'
+EXTERN char_u *p_popt; // 'printoptions'
+EXTERN char_u *p_header; // 'printheader'
+EXTERN int p_prompt; // 'prompt'
+EXTERN char_u *p_guicursor; // 'guicursor'
+EXTERN char_u *p_hf; // 'helpfile'
+EXTERN long p_hh; // 'helpheight'
+EXTERN char_u *p_hlg; // 'helplang'
+EXTERN int p_hid; // 'hidden'
+// Use P_HID to check if a buffer is to be hidden when it is no longer
+// visible in a window.
# define P_HID(buf) (buf_hide(buf))
EXTERN char_u *p_hl; /* 'highlight' */
EXTERN int p_hls; /* 'hlsearch' */
@@ -537,64 +566,74 @@ EXTERN int p_stmp; /* 'shelltemp' */
#ifdef BACKSLASH_IN_FILENAME
EXTERN int p_ssl; /* 'shellslash' */
#endif
-EXTERN char_u *p_stl; /* 'statusline' */
-EXTERN int p_sr; /* 'shiftround' */
-EXTERN char_u *p_shm; /* 'shortmess' */
-EXTERN char_u *p_sbr; /* 'showbreak' */
-EXTERN int p_sc; /* 'showcmd' */
-EXTERN int p_sft; /* 'showfulltag' */
-EXTERN int p_sm; /* 'showmatch' */
-EXTERN int p_smd; /* 'showmode' */
-EXTERN long p_ss; /* 'sidescroll' */
-EXTERN long p_siso; /* 'sidescrolloff' */
-EXTERN int p_scs; /* 'smartcase' */
-EXTERN int p_sta; /* 'smarttab' */
-EXTERN int p_sb; /* 'splitbelow' */
-EXTERN long p_tpm; /* 'tabpagemax' */
-EXTERN char_u *p_tal; /* 'tabline' */
-EXTERN char_u *p_sps; /* 'spellsuggest' */
-EXTERN int p_spr; /* 'splitright' */
-EXTERN int p_sol; /* 'startofline' */
-EXTERN char_u *p_su; /* 'suffixes' */
-EXTERN char_u *p_sws; /* 'swapsync' */
-EXTERN char_u *p_swb; /* 'switchbuf' */
+EXTERN char_u *p_stl; // 'statusline'
+EXTERN int p_sr; // 'shiftround'
+EXTERN char_u *p_shm; // 'shortmess'
+EXTERN char_u *p_sbr; // 'showbreak'
+EXTERN int p_sc; // 'showcmd'
+EXTERN int p_sft; // 'showfulltag'
+EXTERN int p_sm; // 'showmatch'
+EXTERN int p_smd; // 'showmode'
+EXTERN long p_ss; // 'sidescroll'
+EXTERN long p_siso; // 'sidescrolloff'
+EXTERN int p_scs; // 'smartcase'
+EXTERN int p_sta; // 'smarttab'
+EXTERN int p_sb; // 'splitbelow'
+EXTERN long p_tpm; // 'tabpagemax'
+EXTERN char_u *p_tal; // 'tabline'
+EXTERN char_u *p_sps; // 'spellsuggest'
+EXTERN int p_spr; // 'splitright'
+EXTERN int p_sol; // 'startofline'
+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};
@@ -665,6 +704,7 @@ enum {
, BV_DEF
, BV_INC
, BV_EOL
+ , BV_FIXEOL
, BV_EP
, BV_ET
, BV_FENC
@@ -706,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 b22e994efe..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,10 +745,10 @@ 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"}}
+ defaults={if_true={vi="ucs-bom,utf-8,default,latin1"}}
},
{
full_name='fileformat', abbreviation='ff',
@@ -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'},
@@ -799,6 +799,14 @@ return {
defaults={if_true={vi="vert:|,fold:-"}}
},
{
+ full_name='fixendofline', abbreviation='fixeol',
+ type='bool', scope={'buffer'},
+ vi_def=true,
+ redraw={'statuslines'},
+ varname='p_fixeol',
+ defaults={if_true={vi=true}}
+ },
+ {
full_name='fkmap', abbreviation='fk',
type='bool', scope={'global'},
vi_def=true,
@@ -807,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'},
@@ -863,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,
@@ -896,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'},
@@ -951,7 +959,6 @@ return {
type='bool', scope={'global'},
secure=true,
vi_def=true,
- enable_if='HAVE_FSYNC',
varname='p_fs',
defaults={if_true={vi=true}}
},
@@ -965,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',
@@ -988,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',
@@ -996,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'},
@@ -1004,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'},
@@ -1063,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=""}}
@@ -1077,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'},
@@ -1137,20 +1144,6 @@ return {
defaults={if_true={vi=false}}
},
{
- full_name='imactivatefunc', abbreviation='imaf',
- type='string', scope={'global'},
- secure=true,
- vi_def=true,
- enable_if=false,
- },
- {
- full_name='imactivatekey', abbreviation='imak',
- type='string', scope={'global'},
- vi_def=true,
- enable_if=false,
- defaults={if_true={vi=""}}
- },
- {
full_name='imcmdline', abbreviation='imc',
type='bool', scope={'global'},
vi_def=true,
@@ -1187,13 +1180,6 @@ return {
}
},
{
- full_name='imstatusfunc', abbreviation='imsf',
- type='string', scope={'global'},
- secure=true,
- vi_def=true,
- enable_if=false,
- },
- {
full_name='include', abbreviation='inc',
type='string', scope={'global', 'buffer'},
vi_def=true,
@@ -1227,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,
@@ -1278,7 +1264,7 @@ return {
vim=true,
alloced=true,
varname='p_isk',
- defaults={if_true={vi="@,48-57,_", vim=macros('ISK_LATIN1')}}
+ defaults={if_true={vi="@,48-57,_", vim="@,48-57,_,192-255"}}
},
{
full_name='isprint', abbreviation='isp',
@@ -1287,10 +1273,7 @@ return {
vi_def=true,
redraw={'all_windows'},
varname='p_isp',
- defaults={
- condition='MSWIN',
- if_true={vi="@,~-255"},
- if_false={vi=macros("ISP_LATIN1")}
+ defaults={if_true={vi="@,161-255"}
}
},
{
@@ -1314,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',
@@ -1333,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,
@@ -1402,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',
@@ -1417,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'},
@@ -1458,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,
@@ -1598,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,
@@ -1612,11 +1595,11 @@ 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',
- defaults={if_true={vi="octal,hex", vim="hex"}}
+ defaults={if_true={vi="bin,octal,hex", vim="bin,hex"}}
},
{
full_name='number', abbreviation='nu',
@@ -1779,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',
@@ -1852,13 +1835,6 @@ return {
defaults={if_true={vi=2}}
},
{
- full_name='restorescreen', abbreviation='rs',
- type='bool', scope={'global'},
- vi_def=true,
- enable_if=false,
- defaults={if_true={vi=true}}
- },
- {
full_name='revins', abbreviation='ri',
type='bool', scope={'global'},
vi_def=true,
@@ -1901,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,
@@ -1943,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',
@@ -1973,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',
@@ -1981,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',
@@ -1992,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',
@@ -2216,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,
@@ -2226,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,
@@ -2236,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,
@@ -2276,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',
@@ -2284,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,
@@ -2300,15 +2276,8 @@ return {
defaults={if_true={vi=true}}
},
{
- full_name='swapsync', abbreviation='sws',
- type='string', scope={'global'},
- vi_def=true,
- varname='p_sws',
- defaults={if_true={vi="fsync"}}
- },
- {
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',
@@ -2363,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,
@@ -2378,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,
@@ -2407,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,
@@ -2424,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,
@@ -2509,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,
@@ -2580,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',
@@ -2588,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',
@@ -2596,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,
@@ -2641,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',
@@ -2663,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/dl.c b/src/nvim/os/dl.c
index b4a35e203e..fef02cc784 100644
--- a/src/nvim/os/dl.c
+++ b/src/nvim/os/dl.c
@@ -63,7 +63,7 @@ bool os_libcall(const char *libname,
// call the library and save the result
// TODO(aktau): catch signals and use jmp (if available) to handle
- // exceptions. jmp's on UNIX seem to interact trickily with signals as
+ // exceptions. jmp's on Unix seem to interact trickily with signals as
// well. So for now we only support those libraries that are well-behaved.
if (str_out) {
str_str_fn sfn = (str_str_fn) fn;
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 0e052ced55..edc430410c 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -46,7 +46,19 @@ bool os_env_exists(const char *name)
int os_setenv(const char *name, const char *value, int overwrite)
FUNC_ATTR_NONNULL_ALL
{
+#ifdef HAVE_SETENV
return setenv(name, value, overwrite);
+#elif defined(HAVE_PUTENV_S)
+ if (!overwrite && os_getenv(name) != NULL) {
+ return 0;
+ }
+ if (_putenv_s(name, value) == 0) {
+ return 0;
+ }
+ return -1;
+#else
+# error "This system has no implementation available for os_setenv()"
+#endif
}
/// Unset environment variable
@@ -130,12 +142,12 @@ void os_get_hostname(char *hostname, size_t len)
/// - go to that directory
/// - do os_dirname() to get the real name of that directory.
/// This also works with mounts and links.
-/// Don't do this for MS-DOS, it will change the "current dir" for a drive.
+/// Don't do this for Windows, it will change the "current dir" for a drive.
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;
@@ -164,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);
@@ -227,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;
@@ -261,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
{
@@ -278,7 +309,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
}
#if defined(UNIX)
- // Verify that we have found the end of a UNIX ${VAR} style variable
+ // Verify that we have found the end of a Unix ${VAR} style variable
if (src[1] == '{' && *tail != '}') {
var = NULL;
} else {
@@ -292,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;
@@ -302,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
@@ -315,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);
@@ -352,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;
}
@@ -362,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)
@@ -375,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
@@ -393,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;
@@ -422,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;
}
@@ -716,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;
@@ -730,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
}
@@ -754,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 d59b66e773..49a74cf0d1 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 (os_stat(name, &statbuf) == 0) {
+ return NODE_NORMAL;
+ }
+
+#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);
}
@@ -146,17 +234,27 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
}
size_t buf_len = STRLEN(name) + STRLEN(path) + 2;
+
+#ifdef WIN32
+ const char *pathext = os_getenv("PATHEXT");
+ if (!pathext) {
+ pathext = ".com;.exe;.bat;.cmd";
+ }
+
+ buf_len += STRLEN(pathext);
+#endif
+
char_u *buf = xmalloc(buf_len);
// Walk through all entries in $PATH to check if "name" exists there and
// is an executable file.
for (;; ) {
- const char *e = xstrchrnul(path, ':');
+ const char *e = xstrchrnul(path, ENV_SEPCHAR);
// 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.
@@ -169,7 +267,39 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
return true;
}
- if (*e != ':') {
+#ifdef WIN32
+ // Try appending file extensions from $PATHEXT to the name.
+ char *buf_end = xstrchrnul((char *)buf, '\0');
+ for (const char *ext = pathext; *ext; ext++) {
+ // Skip the extension if there is no suffix after a '.'.
+ if (ext[0] == '.' && (ext[1] == '\0' || ext[1] == ';')) {
+ *ext++;
+
+ continue;
+ }
+
+ const char *ext_end = xstrchrnul(ext, ENV_SEPCHAR);
+ STRLCPY(buf_end, ext, ext_end - ext + 1);
+
+ if (is_executable(buf)) {
+ // Check if the caller asked for a copy of the path.
+ if (abspath != NULL) {
+ *abspath = save_absolute_path(buf);
+ }
+
+ xfree(buf);
+
+ return true;
+ }
+
+ if (*ext_end != ENV_SEPCHAR) {
+ break;
+ }
+ ext = ext_end;
+ }
+#endif
+
+ if (*e != ENV_SEPCHAR) {
// End of $PATH without finding any executable called name.
xfree(buf);
return false;
@@ -202,6 +332,19 @@ int os_open(const char* path, int flags, int mode)
return r;
}
+/// Flushes file modifications to disk.
+///
+/// @param fd the file descriptor of the file to flush to disk.
+///
+/// @return `0` on success, a libuv error code on failure.
+int os_fsync(int fd)
+{
+ uv_fs_t fsync_req;
+ int r = uv_fs_fsync(&fs_loop, &fsync_req, fd, NULL);
+ uv_fs_req_cleanup(&fsync_req);
+ return r;
+}
+
/// Get stat information for a file.
///
/// @return libuv return code.
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 ef6b5ff6f5..7687b14f02 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -19,6 +19,7 @@
#include "nvim/getchar.h"
#include "nvim/main.h"
#include "nvim/misc1.h"
+#include "nvim/misc2.h"
#define READ_BUFFER_SIZE 0xfff
#define INPUT_BUFFER_SIZE (READ_BUFFER_SIZE * 4)
@@ -174,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);
@@ -249,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;
}
@@ -357,7 +367,7 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data,
static void process_interrupts(void)
{
- if (mapped_ctrl_c) {
+ if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) {
return;
}
@@ -393,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/os_defs.h b/src/nvim/os/os_defs.h
index 7d77899287..eee0cdd10b 100644
--- a/src/nvim/os/os_defs.h
+++ b/src/nvim/os/os_defs.h
@@ -13,24 +13,7 @@
# include "nvim/os/unix_defs.h"
#endif
-#if defined(DIRSIZ) && !defined(MAXNAMLEN)
-# define MAXNAMLEN DIRSIZ
-#endif
-
-#if defined(UFS_MAXNAMLEN) && !defined(MAXNAMLEN)
-# define MAXNAMLEN UFS_MAXNAMLEN /* for dynix/ptx */
-#endif
-
-#if defined(NAME_MAX) && !defined(MAXNAMLEN)
-# define MAXNAMLEN NAME_MAX /* for Linux before .99p3 */
-#endif
-
-// Default value.
-#ifndef MAXNAMLEN
-# define MAXNAMLEN 512
-#endif
-
-#define BASENAMELEN (MAXNAMLEN - 5)
+#define BASENAMELEN (NAME_MAX - 5)
// Use the system path length if it makes sense.
#if defined(PATH_MAX) && (PATH_MAX > 1000)
@@ -51,25 +34,6 @@
# define DFLT_MAXMEMTOT (10*1024)
#endif
-#if !defined(S_ISDIR) && defined(S_IFDIR)
-# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
-#endif
-#if !defined(S_ISREG) && defined(S_IFREG)
-# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
-#endif
-#if !defined(S_ISBLK) && defined(S_IFBLK)
-# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
-#endif
-#if !defined(S_ISSOCK) && defined(S_IFSOCK)
-# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
-#endif
-#if !defined(S_ISFIFO) && defined(S_IFIFO)
-# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
-#endif
-#if !defined(S_ISCHR) && defined(S_IFCHR)
-# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
-#endif
-
// Note: Some systems need both string.h and strings.h (Savage). However,
// some systems can't handle both, only use string.h in that case.
#include <string.h>
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 57e25560de..f5a1637c94 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -36,7 +36,6 @@ typedef struct {
# include "os/shell.c.generated.h"
#endif
-
/// Builds the argument vector for running the user-configured 'shell' (p_sh)
/// with an optional command prefixed by 'shellcmdflag' (p_shcf).
///
@@ -45,9 +44,10 @@ typedef struct {
/// @return A newly allocated argument vector. It must be freed with
/// `shell_free_argv` when no longer needed.
char **shell_build_argv(const char *cmd, const char *extra_args)
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC
{
- size_t argc = tokenize(p_sh, NULL) + tokenize(p_shcf, NULL);
- char **rv = xmalloc((unsigned)((argc + 4) * sizeof(char *)));
+ size_t argc = tokenize(p_sh, NULL) + (cmd ? tokenize(p_shcf, NULL) : 0);
+ char **rv = xmalloc((argc + 4) * sizeof(*rv));
// Split 'shell'
size_t i = tokenize(p_sh, rv);
@@ -338,24 +338,22 @@ static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data,
/// @param argv The vector that will be filled with copies of the parsed
/// words. It can be NULL if the caller only needs to count words.
/// @return The number of words parsed.
-static size_t tokenize(const char_u *str, char **argv)
+static size_t tokenize(const char_u *const str, char **const argv)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- size_t argc = 0, len;
- char_u *p = (char_u *) str;
+ size_t argc = 0;
+ const char *p = (const char *) str;
while (*p != NUL) {
- len = word_length(p);
+ const size_t len = word_length((const char_u *) p);
if (argv != NULL) {
// Fill the slot
- argv[argc] = xmalloc(len + 1);
- memcpy(argv[argc], p, len);
- argv[argc][len] = NUL;
+ argv[argc] = vim_strnsave_unquoted(p, len);
}
argc++;
- p += len;
- p = skipwhite(p);
+ p = (const char *) skipwhite((char_u *) (p + len));
}
return argc;
@@ -377,6 +375,9 @@ static size_t word_length(const char_u *str)
if (*p == '"') {
// Found a quote character, switch the `inquote` flag
inquote = !inquote;
+ } else if (*p == '\\' && inquote) {
+ p++;
+ length++;
}
p++;
@@ -418,7 +419,8 @@ static void read_input(DynamicBuffer *buf)
// Finished a line, add a NL, unless this line should not have one.
// FIXME need to make this more readable
if (lnum != curbuf->b_op_end.lnum
- || !curbuf->b_p_bin
+ || (!curbuf->b_p_bin
+ && curbuf->b_p_fixeol)
|| (lnum != curbuf->b_no_eol_lnum
&& (lnum !=
curbuf->b_ml.ml_line_count
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/time.c b/src/nvim/os/time.c
index ba1dcf631a..188f0802c9 100644
--- a/src/nvim/os/time.c
+++ b/src/nvim/os/time.c
@@ -93,7 +93,7 @@ struct tm *os_localtime_r(const time_t *restrict clock,
#endif
}
-/// Obtains the current UNIX timestamp and adjusts it to local time
+/// Obtains the current Unix timestamp and adjusts it to local time.
///
/// @param result Pointer to a 'struct tm' where the result should be placed
/// @return A pointer to a 'struct tm' in the current time zone (the 'result'
@@ -104,7 +104,7 @@ struct tm *os_get_localtime(struct tm *result) FUNC_ATTR_NONNULL_ALL
return os_localtime_r(&rawtime, result);
}
-/// Obtains the current UNIX timestamp
+/// Obtains the current Unix timestamp.
///
/// @return Seconds since epoch.
Timestamp os_time(void)
diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h
index e3ba3262f4..690a39c3cd 100644
--- a/src/nvim/os/unix_defs.h
+++ b/src/nvim/os/unix_defs.h
@@ -1,13 +1,12 @@
#ifndef NVIM_OS_UNIX_DEFS_H
#define NVIM_OS_UNIX_DEFS_H
+// Windows doesn't have unistd.h, so we include it here to avoid numerous
+// instances of `#ifdef WIN32'.
#include <unistd.h>
-#include <signal.h>
-// Defines BSD, if it's a BSD system.
-#ifdef HAVE_SYS_PARAM_H
-# include <sys/param.h>
-#endif
+// POSIX.1-2008 says that NAME_MAX should be in here
+#include <limits.h>
#define TEMP_DIR_NAMES {"$TMPDIR", "/tmp", ".", "~"}
#define TEMP_FILE_PATH_MAXLEN 256
@@ -17,4 +16,7 @@
// Special wildcards that need to be handled by the shell.
#define SPECIAL_WILDCHAR "`'{"
+// Separator character for environment variables.
+#define ENV_SEPCHAR ':'
+
#endif // NVIM_OS_UNIX_DEFS_H
diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c
index 637a86c74f..8ebb7562ef 100644
--- a/src/nvim/os/users.c
+++ b/src/nvim/os/users.c
@@ -42,17 +42,17 @@ int os_get_usernames(garray_T *users)
int os_get_user_name(char *s, size_t len)
{
#ifdef UNIX
- return os_get_uname(getuid(), s, len);
+ return os_get_uname((uv_uid_t)getuid(), s, len);
#else
// TODO(equalsraf): Windows GetUserName()
- return os_get_uname(0, s, len);
+ return os_get_uname((uv_uid_t)0, s, len);
#endif
}
// Insert user name for "uid" in s[len].
// Return OK if a name found.
// If the name is not found, write the uid into s[len] and return FAIL.
-int os_get_uname(uid_t uid, char *s, size_t len)
+int os_get_uname(uv_uid_t uid, char *s, size_t len)
{
#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
struct passwd *pw;
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index 32960dfbe9..6a29f86e79 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -1,13 +1,33 @@
#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>
+#include <stdio.h>
+
+// Windows does not have S_IFLNK but libuv defines it
+// and sets the flag for us when calling uv_fs_stat.
+#include <uv.h>
+
+#define NAME_MAX _MAX_PATH
#define TEMP_DIR_NAMES {"$TMP", "$TEMP", "$USERPROFILE", ""}
#define TEMP_FILE_PATH_MAXLEN _MAX_PATH
+#define FNAME_ILLEGAL "\"*?><|"
+
+// Separator character for environment variables.
+#define ENV_SEPCHAR ';'
+
#define USE_CRNL
+// We have our own RGB macro in macros.h.
+#undef RGB
+
#ifdef _MSC_VER
# ifndef inline
# define inline __inline
@@ -15,8 +35,19 @@
# ifndef restrict
# define restrict __restrict
# endif
+# ifndef STDOUT_FILENO
+# define STDOUT_FILENO _fileno(stdout)
+# endif
+# ifndef STDERR_FILENO
+# define STDERR_FILENO _fileno(stderr)
+# endif
+# ifndef S_IXUSR
+# define S_IXUSR S_IEXEC
+# endif
#endif
+#define BACKSLASH_IN_FILENAME
+
#ifdef _MSC_VER
typedef SSIZE_T ssize_t;
#endif
@@ -29,4 +60,30 @@ typedef SSIZE_T ssize_t;
# endif
#endif
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFIFO)
+# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#endif
+
#endif // NVIM_OS_WIN_DEFS_H
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index 62b264046c..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"
@@ -44,10 +43,6 @@
#include "nvim/os/signal.h"
#include "nvim/msgpack_rpc/helpers.h"
-#ifdef HAVE_STROPTS_H
-# include <stropts.h>
-#endif
-
#ifdef HAVE_SELINUX
# include <selinux/selinux.h>
static int selinux_enabled = -1;
@@ -141,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;
@@ -179,13 +154,6 @@ void mch_exit(int r)
exit(r);
}
-#ifndef SEEK_SET
-# define SEEK_SET 0
-#endif
-#ifndef SEEK_END
-# define SEEK_END 2
-#endif
-
#define SHELL_SPECIAL (char_u *)"\t \"&'$;<>()\\|"
/// Does wildcard pattern matching using the shell.
@@ -608,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 253035ed99..41fd69f238 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -196,7 +196,7 @@ char_u *get_past_head(char_u *path)
int vim_ispathsep(int c)
{
#ifdef UNIX
- return c == '/'; /* UNIX has ':' inside file names */
+ return c == '/'; // Unix has ':' inside file names
#else
# ifdef BACKSLASH_IN_FILENAME
return c == ':' || c == '/' || 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
@@ -579,11 +570,15 @@ static size_t do_path_expand(garray_T *gap, const char_u *path,
s = p + 1;
} else if (path_end >= path + wildoff
&& (vim_strchr((char_u *)"*?[{~$", *path_end) != NULL
+#ifndef WIN32
|| (!p_fic && (flags & EW_ICASE)
- && isalpha(PTR2CHAR(path_end)))))
+ && isalpha(PTR2CHAR(path_end)))
+#endif
+ )) {
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;
@@ -608,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;
@@ -641,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;
@@ -652,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))) {
@@ -677,11 +675,16 @@ static size_t do_path_expand(garray_T *gap, const char_u *path,
/* remove backslashes for the remaining components only */
(void)do_path_expand(gap, buf, len + 1, flags, false);
} else {
- /* no more wildcards, check if there is a match */
- /* remove backslashes for the remaining components only */
- if (*path_end != NUL)
+ FileInfo file_info;
+
+ // no more wildcards, check if there is a match
+ // remove backslashes for the remaining components only
+ if (*path_end != NUL) {
backslash_halve(buf + len + 1);
- if (os_file_exists(buf)) { /* add existing file */
+ }
+ // add existing file or symbolic link
+ if ((flags & EW_ALLLINKS) ? os_fileinfo_link((char *)buf, &file_info)
+ : os_file_exists(buf)) {
addfile(gap, buf, flags);
}
}
@@ -693,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;
}
@@ -726,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
}
/*
@@ -760,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, " ,");
@@ -777,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;
@@ -850,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;
@@ -885,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];
@@ -898,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)
@@ -989,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)) {
@@ -1009,7 +1001,7 @@ static char_u *gettail_dir(char_u *fname)
}
mb_ptr_adv(p);
}
- return dir_end;
+ return (char_u *)dir_end;
}
@@ -1103,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;
@@ -1130,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
@@ -1145,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)
@@ -1197,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;
}
}
}
@@ -1225,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;
}
@@ -1237,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);
@@ -1264,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) {
@@ -1275,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;
@@ -1290,26 +1284,52 @@ expand_backtick (
return cnt;
}
-/*
- * Add a file to a file list. Accepted flags:
- * EW_DIR add directories
- * EW_FILE add files
- * EW_EXEC add executable files
- * EW_NOTFOUND add even when it doesn't exist
- * EW_ADDSLASH add slash after directory name
- */
-void
-addfile (
+#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
+// EW_EXEC add executable files
+// EW_NOTFOUND add even when it doesn't exist
+// EW_ADDSLASH add slash after directory name
+// EW_ALLLINKS add symlink also when the referred file does not exist
+void addfile(
garray_T *gap,
char_u *f, /* filename */
int flags
)
{
bool isdir;
+ FileInfo file_info;
- /* if the file/dir doesn't exist, may not add it */
- if (!(flags & EW_NOTFOUND) && !os_file_exists(f))
+ // 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))) {
return;
+ }
#ifdef FNAME_ILLEGAL
/* if the file/dir contains illegal characters, don't add it */
@@ -1321,9 +1341,12 @@ 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);
@@ -1387,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);
@@ -1502,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;
}
@@ -1526,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);
@@ -1539,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
@@ -1548,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);
@@ -1557,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;
@@ -1616,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;
@@ -1764,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;
}
@@ -1800,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));
@@ -1927,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;
@@ -1935,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)
@@ -1947,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);
}
@@ -1967,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;
@@ -1997,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);
@@ -2016,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;
}
}
@@ -2029,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;
@@ -2071,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) {
@@ -2109,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 628ea335ed..4e466d1b71 100644
--- a/src/nvim/path.h
+++ b/src/nvim/path.h
@@ -20,6 +20,11 @@
#define EW_KEEPDOLLAR 0x800 /* do not escape $, $var is expanded */
/* 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 7fe61c45bc..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."
@@ -25,9 +25,8 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
#: ../api/private/helpers.c:201
-#, fuzzy
msgid "Unable to get option value"
-msgstr "impossibile ottenere il valore di opzione"
+msgstr "Impossibile ottenere il valore di opzione"
#: ../api/private/helpers.c:204
msgid "internal error: unknown option type"
@@ -105,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"
@@ -129,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
@@ -149,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"
@@ -182,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]"
@@ -251,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"
@@ -347,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)"
@@ -375,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)"
@@ -453,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"
@@ -681,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"
@@ -755,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()"
@@ -775,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
@@ -827,18 +826,16 @@ msgid "sort() argument"
msgstr "argomento di sort()"
#: ../eval.c:13721
-#, fuzzy
msgid "uniq() argument"
-msgstr "argomento di add()"
+msgstr "argomento di uniq()"
#: ../eval.c:13776
msgid "E702: Sort compare function failed"
msgstr "E702: Funzione confronto nel sort non riuscita"
#: ../eval.c:13806
-#, fuzzy
msgid "E882: Uniq compare function failed"
-msgstr "E702: Funzione confronto nel sort non riuscita"
+msgstr "E882: Funzione di comparazione 'uniq' fallita"
#: ../eval.c:14085
msgid "(Invalid)"
@@ -963,16 +960,12 @@ msgid "E129: Function name required"
msgstr "E129: Nome funzione necessario"
#: ../eval.c:17824
-#, fuzzy, c-format
msgid "E128: Function name must start with a capital or \"s:\": %s"
-msgstr ""
-"E128: Il nome funzione deve iniziare con una maiuscola o contenere ':': %s"
+msgstr "E128: Il nome funzione deve iniziare con una maiuscola o \"s:\": %s"
#: ../eval.c:17833
-#, fuzzy, c-format
msgid "E884: Function name cannot contain a colon: %s"
-msgstr ""
-"E128: Il nome funzione deve iniziare con una maiuscola o contenere ':': %s"
+msgstr "E884: Il nome funzione non può contenere una virgola: %s"
#: ../eval.c:18336
#, c-format
@@ -1050,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"
@@ -1077,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"
@@ -1246,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"
@@ -1264,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
@@ -1362,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)"
@@ -1377,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
@@ -1387,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
@@ -1401,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}\""
@@ -1424,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
@@ -1472,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
@@ -1482,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
@@ -1511,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"
@@ -1613,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"
@@ -1642,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"
@@ -1665,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"
@@ -1673,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
@@ -1822,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
@@ -1968,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"
@@ -1980,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"
@@ -2098,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]"
@@ -2111,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]"
@@ -2144,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"
@@ -2220,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
@@ -2234,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]"
@@ -2278,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!"
@@ -2310,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"
@@ -2332,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"
@@ -2375,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."
@@ -2384,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."
@@ -2393,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."
@@ -2402,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 ""
@@ -2520,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
@@ -2667,18 +2674,17 @@ msgid "E17: \"%s\" is a directory"
msgstr "E17: \"%s\" è una directory"
#: ../globals.h:1020
-#, fuzzy
msgid "E900: Invalid job id"
-msgstr "E49: Quantità di 'scroll' non valida"
+msgstr "E900: 'Job id' non valido"
#: ../globals.h:1021
msgid "E901: Job table is full"
-msgstr ""
+msgstr "E901: Job table piena"
#: ../globals.h:1022
#, c-format
msgid "E902: \"%s\" is not an executable"
-msgstr ""
+msgstr "E902: \"%s\" non è un esegubile"
#: ../globals.h:1024
#, c-format
@@ -2687,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"
@@ -2703,7 +2709,7 @@ msgstr "E22: Script troppo nidificati"
#: ../globals.h:1031
msgid "E23: No alternate file"
-msgstr "E23: Nessun file alternato"
+msgstr "E23: Nessun file alternativo"
#: ../globals.h:1032
msgid "E24: No such abbreviation"
@@ -2728,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"
@@ -2791,9 +2797,8 @@ msgid "E37: No write since last change (add ! to override)"
msgstr "E37: Non salvato dopo modifica (aggiungi ! per eseguire comunque)"
#: ../globals.h:1055
-#, fuzzy
msgid "E37: No write since last change"
-msgstr "[Non salvato dopo l'ultima modifica]\n"
+msgstr "E37: Non salvato dall'ultima modifica"
#: ../globals.h:1056
msgid "E38: Null argument"
@@ -2857,8 +2862,7 @@ msgstr "E46: Non posso cambiare la variabile read-only \"%s\""
#: ../globals.h:1075
#, c-format
msgid "E794: Cannot set variable in the sandbox: \"%s\""
-msgstr ""
-"E794: Non posso impostare la variabile read-only in ambiente protetto: \"%s\""
+msgstr "E794: Non posso impostare la variabile read-only in ambiente protetto: \"%s\""
#: ../globals.h:1076
msgid "E47: Error while reading errorfile"
@@ -2957,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"
@@ -3270,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
@@ -3336,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
@@ -3538,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"
@@ -3599,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
@@ -3608,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
@@ -3617,7 +3626,7 @@ msgid ""
"change line col text"
msgstr ""
"\n"
-"modif linea col testo"
+"modif riga col testo"
#: ../mark.c:1238
msgid ""
@@ -3777,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
@@ -3786,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"
@@ -3798,7 +3807,7 @@ msgstr "???BLOCCO VUOTO"
#: ../memline.c:1103
msgid "???LINES MISSING"
-msgstr "???LINEE MANCANTI"
+msgstr "???RIGHE MANCANTI"
#: ../memline.c:1128
#, c-format
@@ -3811,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"
@@ -3829,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."
@@ -3990,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"
@@ -4020,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"
@@ -4033,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"
@@ -4252,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
@@ -4274,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 --"
@@ -4282,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"
@@ -4334,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): "
@@ -4347,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)"
@@ -4386,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"
@@ -4415,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"
@@ -4457,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
@@ -4513,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; "
@@ -4524,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
@@ -4533,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
@@ -4542,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
@@ -4551,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
@@ -4597,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'"
@@ -4675,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
@@ -4832,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"
@@ -4869,9 +4890,8 @@ msgid "E777: String or List expected"
msgstr "E777: aspettavo Stringa o Lista"
#: ../regexp.c:359
-#, fuzzy, c-format
msgid "E369: invalid item in %s%%[]"
-msgstr "E239: Testo 'sign' non valido: %s"
+msgstr "E369: elemento non valido in %s%%[]"
#: ../regexp.c:374
#, c-format
@@ -4984,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 "
@@ -4992,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"
@@ -5004,7 +5032,7 @@ msgstr "E866: (NFA regexp) %c fuori posto"
#: ../regexp_nfa.c:242
#, c-format
msgid "E877: (NFA regexp) Invalid character class: %<PRId64>"
-msgstr ""
+msgstr "E877: (NFA regexp) Classe di caratteri non valida: %<PRId64>"
#: ../regexp_nfa.c:1261
#, c-format
@@ -5124,7 +5152,7 @@ msgstr " VISUALE"
#: ../screen.c:7470
msgid " VISUAL LINE"
-msgstr " VISUALE LINEA"
+msgstr " VISUALE RIGA"
#: ../screen.c:7471
msgid " VISUAL BLOCK"
@@ -5136,7 +5164,7 @@ msgstr " SELEZIONA"
#: ../screen.c:7473
msgid " SELECT LINE"
-msgstr " SELEZIONA LINEA"
+msgstr " SELEZIONA RIGA"
#: ../screen.c:7474
msgid " SELECT BLOCK"
@@ -5202,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"
@@ -5246,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"
@@ -5272,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
@@ -5298,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
@@ -5308,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
@@ -5318,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
@@ -5332,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
@@ -5341,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
@@ -5386,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"
@@ -5442,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
@@ -5477,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
@@ -5507,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
@@ -5594,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
@@ -5611,14 +5639,12 @@ msgid "E765: 'spellfile' does not have %<PRId64> entries"
msgstr "E765: 'spellfile' non ha %<PRId64> elementi"
#: ../spell.c:8074
-#, fuzzy, c-format
msgid "Word '%.*s' removed from %s"
-msgstr "Parola rimossa da %s"
+msgstr "Parola '%.*s' rimossa da %s"
#: ../spell.c:8117
-#, fuzzy, c-format
msgid "Word '%.*s' added to %s"
-msgstr "Parola aggiunta a %s"
+msgstr "Parola '%.*s' aggiunta a %s"
#: ../spell.c:8381
msgid "E763: Word characters differ between spell files"
@@ -5713,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 ""
@@ -5758,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"
@@ -5822,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
@@ -6011,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
@@ -6020,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
@@ -6100,9 +6126,8 @@ msgstr "Vim: Errore leggendo l'input, esco...\n"
#. This happens when the FileChangedRO autocommand changes the
#. * file in a way it becomes shorter.
#: ../undo.c:379
-#, fuzzy
msgid "E881: Line count changed unexpectedly"
-msgstr "E834: Il conteggio delle righe è inaspettatamente cambiato"
+msgstr "E881: Il conteggio delle righe è inaspettatamente cambiato"
#: ../undo.c:627
#, c-format
@@ -6191,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"
@@ -6253,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 ""
@@ -6453,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"
@@ -6538,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"
@@ -6662,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"
@@ -6719,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..."
@@ -6844,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'"
@@ -6871,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"
@@ -6879,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."
@@ -6889,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"
@@ -7024,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"
@@ -7033,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: "
@@ -7081,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"
@@ -7307,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"
@@ -7379,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: "
@@ -7405,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>, )"
@@ -7413,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"
@@ -7451,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"
@@ -7552,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"
@@ -7624,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!"
@@ -7889,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"
@@ -7976,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"
@@ -7999,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"
@@ -8019,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"
@@ -8056,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 8e6ae46a3b..17cb8a86aa 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"
@@ -200,9 +199,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 +218,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 +276,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 +529,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 +543,7 @@ restofline:
errmsg[0] = NUL;
lnum = 0;
col = 0;
- use_viscol = FALSE;
+ use_viscol = false;
enr = -1;
type = 0;
tail = NULL;
@@ -555,25 +552,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 +624,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 +648,7 @@ restofline:
break;
}
}
- multiscan = FALSE;
+ multiscan = false;
if (fmt_ptr == NULL || idx == 'D' || idx == 'X') {
if (fmt_ptr != NULL) {
@@ -667,20 +662,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 +698,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 +717,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 +866,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));
@@ -1202,7 +1202,7 @@ static void qf_clean_dir_stack(struct dir_stack_T **stackptr)
/*
* Check in which directory of the directory stack the given file can be
* found.
- * Returns a pointer to the directory name or NULL if not found
+ * Returns a pointer to the directory name or NULL if not found.
* Cleans up intermediate directory entries.
*
* TODO: How to solve the following problem?
@@ -1606,13 +1606,12 @@ win_found:
}
if (qf_ptr->qf_col > 0) {
curwin->w_cursor.col = qf_ptr->qf_col - 1;
- if (qf_ptr->qf_viscol == TRUE) {
- /*
- * Check each character from the beginning of the error
- * line up to the error column. For each tab character
- * found, reduce the error column value by the length of
- * a tab character.
- */
+ curwin->w_cursor.coladd = 0;
+ if (qf_ptr->qf_viscol == true) {
+ // Check each character from the beginning of the error
+ // line up to the error column. For each tab character
+ // found, reduce the error column value by the length of
+ // a tab character.
line = get_cursor_line_ptr();
screen_col = 0;
for (char_col = 0; char_col < curwin->w_cursor.col; ++char_col) {
@@ -1658,12 +1657,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)
@@ -1828,10 +1828,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) {
@@ -1883,6 +1885,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;
}
@@ -1949,7 +1953,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;
}
@@ -2037,12 +2041,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.
@@ -2117,14 +2122,10 @@ void ex_copen(exarg_T *eap)
prevwin = win;
}
- /*
- * Fill the buffer with the quickfix list.
- */
- qf_fill_buffer(qi);
+ qf_set_title_var(qi);
- if (qi->qf_lists[qi->qf_curlist].qf_title != NULL) {
- qf_set_title_var(qi);
- }
+ // Fill the buffer with the quickfix list.
+ qf_fill_buffer(qi);
curwin->w_cursor.lnum = qi->qf_lists[qi->qf_curlist].qf_index;
curwin->w_cursor.col = 0;
@@ -2256,16 +2257,13 @@ static void qf_update_buffer(qf_info_T *qi)
/* set curwin/curbuf to buf and save a few things */
aucmd_prepbuf(&aco, buf);
- qf_fill_buffer(qi);
-
- if (qi->qf_lists[qi->qf_curlist].qf_title != NULL
- && (win = qf_find_win(qi)) != NULL) {
+ if ((win = qf_find_win(qi)) != NULL) {
curwin_save = curwin;
curwin = win;
qf_set_title_var(qi);
curwin = curwin_save;
-
}
+ qf_fill_buffer(qi);
/* restore curwin/curbuf and a few other things */
aucmd_restbuf(&aco);
@@ -2274,10 +2272,13 @@ static void qf_update_buffer(qf_info_T *qi)
}
}
+// Set "w:quickfix_title" if "qi" has a title.
static void qf_set_title_var(qf_info_T *qi)
{
- set_internal_string_var((char_u *)"w:quickfix_title",
- qi->qf_lists[qi->qf_curlist].qf_title);
+ if (qi->qf_lists[qi->qf_curlist].qf_title != NULL) {
+ set_internal_string_var((char_u *)"w:quickfix_title",
+ qi->qf_lists[qi->qf_curlist].qf_title);
+ }
}
/*
@@ -2304,13 +2305,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) {
@@ -2436,8 +2439,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;
@@ -2475,29 +2476,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
@@ -2551,11 +2551,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);
@@ -2571,9 +2571,159 @@ static char_u *get_mef_name(void)
return name;
}
+/// Returns the number of valid entries in the current quickfix/location list.
+size_t qf_get_size(exarg_T *eap)
+ FUNC_ATTR_NONNULL_ALL
+{
+ qf_info_T *qi = &ql_info;
+ if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) {
+ // Location list.
+ qi = GET_LOC_LIST(curwin);
+ if (qi == NULL) {
+ return 0;
+ }
+ }
+
+ int prev_fnum = 0;
+ size_t sz = 0;
+ qfline_T *qfp;
+ size_t i;
+ assert(qi->qf_lists[qi->qf_curlist].qf_count >= 0);
+ for (i = 0, qfp = qi->qf_lists[qi->qf_curlist].qf_start;
+ i < (size_t)qi->qf_lists[qi->qf_curlist].qf_count && qfp != NULL;
+ i++, qfp = qfp->qf_next) {
+ if (!qfp->qf_valid) {
+ continue;
+ }
+
+ if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo) {
+ // Count all valid entries.
+ sz++;
+ } else if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) {
+ // Count the number of files.
+ sz++;
+ prev_fnum = qfp->qf_fnum;
+ }
+ }
+
+ return sz;
+}
+
+/// Returns the current index of the quickfix/location list.
+/// Returns 0 if there is an error.
+size_t qf_get_cur_idx(exarg_T *eap)
+ FUNC_ATTR_NONNULL_ALL
+{
+ qf_info_T *qi = &ql_info;
+
+ if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) {
+ // Location list.
+ qi = GET_LOC_LIST(curwin);
+ if (qi == NULL) {
+ return 0;
+ }
+ }
+
+ assert(qi->qf_lists[qi->qf_curlist].qf_index >= 0);
+ return (size_t)qi->qf_lists[qi->qf_curlist].qf_index;
+}
+
+/// Returns the current index in the quickfix/location list,
+/// counting only valid entries.
+/// Returns 1 if there are no valid entries.
+int qf_get_cur_valid_idx(exarg_T *eap)
+ FUNC_ATTR_NONNULL_ALL
+{
+ qf_info_T *qi = &ql_info;
+
+ if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) {
+ // Location list.
+ qi = GET_LOC_LIST(curwin);
+ if (qi == NULL) {
+ return 1;
+ }
+ }
+
+ qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist];
+
+ // Check if the list has valid errors.
+ if (qfl->qf_count <= 0 || qfl->qf_nonevalid) {
+ return 1;
+ }
+
+ int prev_fnum = 0;
+ int eidx = 0;
+ qfline_T *qfp;
+ size_t i;
+ assert(qfl->qf_index >= 0);
+ for (i = 1, qfp = qfl->qf_start;
+ i <= (size_t)qfl->qf_index && qfp != NULL;
+ i++, qfp = qfp->qf_next) {
+ if (!qfp->qf_valid) {
+ continue;
+ }
+
+ if (eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
+ if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) {
+ // Count the number of files.
+ eidx++;
+ prev_fnum = qfp->qf_fnum;
+ }
+ } else {
+ eidx++;
+ }
+ }
+
+ return eidx != 0 ? eidx : 1;
+}
+
+/// Get the 'n'th valid error entry in the quickfix or location list.
+///
+/// Used by :cdo, :ldo, :cfdo and :lfdo commands.
+/// For :cdo and :ldo, returns the 'n'th valid error entry.
+/// For :cfdo and :lfdo, returns the 'n'th valid file entry.
+static size_t qf_get_nth_valid_entry(qf_info_T *qi, size_t n, bool fdo)
+ FUNC_ATTR_NONNULL_ALL
+{
+ qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist];
+
+ // Check if the list has valid errors.
+ if (qfl->qf_count <= 0 || qfl->qf_nonevalid) {
+ return 1;
+ }
+
+ int prev_fnum = 0;
+ size_t eidx = 0;
+ size_t i;
+ qfline_T *qfp;
+ assert(qfl->qf_count >= 0);
+ for (i = 1, qfp = qfl->qf_start;
+ i <= (size_t)qfl->qf_count && qfp != NULL;
+ i++, qfp = qfp->qf_next) {
+ if (qfp->qf_valid) {
+ if (fdo) {
+ if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) {
+ // Count the number of files.
+ eidx++;
+ prev_fnum = qfp->qf_fnum;
+ }
+ } else {
+ eidx++;
+ }
+ }
+
+ if (eidx == n) {
+ break;
+ }
+ }
+
+ return i <= (size_t)qfl->qf_count ? i : 1;
+}
+
/*
* ":cc", ":crewind", ":cfirst" and ":clast".
* ":ll", ":lrewind", ":lfirst" and ":llast".
+ * ":cdo", ":ldo", ":cfdo" and ":lfdo".
*/
void ex_cc(exarg_T *eap)
{
@@ -2582,7 +2732,10 @@ void ex_cc(exarg_T *eap)
if (eap->cmdidx == CMD_ll
|| eap->cmdidx == CMD_lrewind
|| eap->cmdidx == CMD_lfirst
- || eap->cmdidx == CMD_llast) {
+ || eap->cmdidx == CMD_llast
+ || eap->cmdidx == CMD_llast
+ || eap->cmdidx == CMD_ldo
+ || eap->cmdidx == CMD_lfdo) {
qi = GET_LOC_LIST(curwin);
if (qi == NULL) {
EMSG(_(e_loclist));
@@ -2590,21 +2743,42 @@ void ex_cc(exarg_T *eap)
}
}
- qf_jump(qi, 0,
- eap->addr_count > 0
- ? (int)eap->line2
- : (eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll)
- ? 0
- : (eap->cmdidx == CMD_crewind || eap->cmdidx == CMD_lrewind
- || eap->cmdidx == CMD_cfirst || eap->cmdidx == CMD_lfirst)
- ? 1
- : 32767,
- eap->forceit);
+ int errornr;
+ if (eap->addr_count > 0) {
+ errornr = (int)eap->line2;
+ } else if (eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll) {
+ errornr = 0;
+ } else if (eap->cmdidx == CMD_crewind || eap->cmdidx == CMD_lrewind
+ || eap->cmdidx == CMD_cfirst || eap->cmdidx == CMD_lfirst) {
+ errornr = 1;
+ } else {
+ errornr = 32767;
+ }
+
+ // 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) {
+ size_t n;
+ if (eap->addr_count > 0) {
+ assert(eap->line1 >= 0);
+ n = (size_t)eap->line1;
+ } else {
+ n = 1;
+ }
+ size_t valid_entry = qf_get_nth_valid_entry(qi, n,
+ eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo);
+ assert(valid_entry <= INT_MAX);
+ errornr = (int)valid_entry;
+ }
+
+ qf_jump(qi, 0, errornr, eap->forceit);
}
/*
* ":cnext", ":cnfile", ":cNext" and ":cprevious".
* ":lnext", ":lNext", ":lprevious", ":lnfile", ":lNfile" and ":lpfile".
+ * ":cdo", ":ldo", ":cfdo" and ":lfdo".
*/
void ex_cnext(exarg_T *eap)
{
@@ -2615,7 +2789,10 @@ void ex_cnext(exarg_T *eap)
|| eap->cmdidx == CMD_lprevious
|| eap->cmdidx == CMD_lnfile
|| eap->cmdidx == CMD_lNfile
- || eap->cmdidx == CMD_lpfile) {
+ || eap->cmdidx == CMD_lpfile
+ || eap->cmdidx == CMD_lpfile
+ || eap->cmdidx == CMD_ldo
+ || eap->cmdidx == CMD_lfdo) {
qi = GET_LOC_LIST(curwin);
if (qi == NULL) {
EMSG(_(e_loclist));
@@ -2623,15 +2800,26 @@ void ex_cnext(exarg_T *eap)
}
}
- qf_jump(qi, (eap->cmdidx == CMD_cnext || eap->cmdidx == CMD_lnext)
+ int errornr;
+ 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;
+ }
+
+ qf_jump(qi, (eap->cmdidx == CMD_cnext || eap->cmdidx == CMD_lnext
+ || eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo)
? FORWARD
- : (eap->cmdidx == CMD_cnfile || eap->cmdidx == CMD_lnfile)
+ : (eap->cmdidx == CMD_cnfile || eap->cmdidx == CMD_lnfile
+ || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
? FORWARD_FILE
: (eap->cmdidx == CMD_cpfile || eap->cmdidx == CMD_lpfile
|| eap->cmdidx == CMD_cNfile || eap->cmdidx == CMD_lNfile)
? BACKWARD_FILE
: BACKWARD,
- eap->addr_count > 0 ? (int)eap->line2 : 1, eap->forceit);
+ errornr, eap->forceit);
}
/*
@@ -2714,7 +2902,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;
@@ -2784,16 +2972,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)
@@ -3271,18 +3461,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);
@@ -3309,21 +3493,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. */
@@ -3332,22 +3517,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 e2c4b590d0..886a48e7c5 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -796,13 +796,14 @@ static void reg_equi_class(int c)
if (enc_utf8 || STRCMP(p_enc, "latin1") == 0
|| STRCMP(p_enc, "iso-8859-15") == 0) {
switch (c) {
- case 'A': case '\300': case '\301': case '\302':
+ // Do not use '\300' style, it results in a negative number.
+ case 'A': case 0xc0: case 0xc1: case 0xc2:
+ case 0xc3: case 0xc4: case 0xc5:
CASEMBC(0x100) CASEMBC(0x102) CASEMBC(0x104) CASEMBC(0x1cd)
CASEMBC(0x1de) CASEMBC(0x1e0) CASEMBC(0x1ea2)
- case '\303': case '\304': case '\305':
- regmbc('A'); regmbc('\300'); regmbc('\301');
- regmbc('\302'); regmbc('\303'); regmbc('\304');
- regmbc('\305');
+ regmbc('A'); regmbc(0xc0); regmbc(0xc1);
+ regmbc(0xc2); regmbc(0xc3); regmbc(0xc4);
+ regmbc(0xc5);
REGMBC(0x100) REGMBC(0x102) REGMBC(0x104)
REGMBC(0x1cd) REGMBC(0x1de) REGMBC(0x1e0)
REGMBC(0x1ea2)
@@ -810,9 +811,9 @@ static void reg_equi_class(int c)
case 'B': CASEMBC(0x1e02) CASEMBC(0x1e06)
regmbc('B'); REGMBC(0x1e02) REGMBC(0x1e06)
return;
- case 'C': case '\307':
+ case 'C': case 0xc7:
CASEMBC(0x106) CASEMBC(0x108) CASEMBC(0x10a) CASEMBC(0x10c)
- regmbc('C'); regmbc('\307');
+ regmbc('C'); regmbc(0xc7);
REGMBC(0x106) REGMBC(0x108) REGMBC(0x10a)
REGMBC(0x10c)
return;
@@ -821,11 +822,11 @@ static void reg_equi_class(int c)
regmbc('D'); REGMBC(0x10e) REGMBC(0x110)
REGMBC(0x1e0a) REGMBC(0x1e0e) REGMBC(0x1e10)
return;
- case 'E': case '\310': case '\311': case '\312': case '\313':
+ case 'E': case 0xc8: case 0xc9: case 0xca: case 0xcb:
CASEMBC(0x112) CASEMBC(0x114) CASEMBC(0x116) CASEMBC(0x118)
CASEMBC(0x11a) CASEMBC(0x1eba) CASEMBC(0x1ebc)
- regmbc('E'); regmbc('\310'); regmbc('\311');
- regmbc('\312'); regmbc('\313');
+ regmbc('E'); regmbc(0xc8); regmbc(0xc9);
+ regmbc(0xca); regmbc(0xcb);
REGMBC(0x112) REGMBC(0x114) REGMBC(0x116)
REGMBC(0x118) REGMBC(0x11a) REGMBC(0x1eba)
REGMBC(0x1ebc)
@@ -845,11 +846,11 @@ static void reg_equi_class(int c)
regmbc('H'); REGMBC(0x124) REGMBC(0x126)
REGMBC(0x1e22) REGMBC(0x1e26) REGMBC(0x1e28)
return;
- case 'I': case '\314': case '\315': case '\316': case '\317':
+ case 'I': case 0xcc: case 0xcd: case 0xce: case 0xcf:
CASEMBC(0x128) CASEMBC(0x12a) CASEMBC(0x12c) CASEMBC(0x12e)
CASEMBC(0x130) CASEMBC(0x1cf) CASEMBC(0x1ec8)
- regmbc('I'); regmbc('\314'); regmbc('\315');
- regmbc('\316'); regmbc('\317');
+ regmbc('I'); regmbc(0xcc); regmbc(0xcd);
+ regmbc(0xce); regmbc(0xcf);
REGMBC(0x128) REGMBC(0x12a) REGMBC(0x12c)
REGMBC(0x12e) REGMBC(0x130) REGMBC(0x1cf)
REGMBC(0x1ec8)
@@ -871,20 +872,20 @@ static void reg_equi_class(int c)
case 'M': CASEMBC(0x1e3e) CASEMBC(0x1e40)
regmbc('M'); REGMBC(0x1e3e) REGMBC(0x1e40)
return;
- case 'N': case '\321':
+ case 'N': case 0xd1:
CASEMBC(0x143) CASEMBC(0x145) CASEMBC(0x147) CASEMBC(0x1e44)
CASEMBC(0x1e48)
- regmbc('N'); regmbc('\321');
+ regmbc('N'); regmbc(0xd1);
REGMBC(0x143) REGMBC(0x145) REGMBC(0x147)
REGMBC(0x1e44) REGMBC(0x1e48)
return;
- case 'O': case '\322': case '\323': case '\324': case '\325':
- case '\326': case '\330':
+ case 'O': case 0xd2: case 0xd3: case 0xd4: case 0xd5:
+ case 0xd6: case 0xd8:
CASEMBC(0x14c) CASEMBC(0x14e) CASEMBC(0x150) CASEMBC(0x1a0)
CASEMBC(0x1d1) CASEMBC(0x1ea) CASEMBC(0x1ec) CASEMBC(0x1ece)
- regmbc('O'); regmbc('\322'); regmbc('\323');
- regmbc('\324'); regmbc('\325'); regmbc('\326');
- regmbc('\330');
+ regmbc('O'); regmbc(0xd2); regmbc(0xd3);
+ regmbc(0xd4); regmbc(0xd5); regmbc(0xd6);
+ regmbc(0xd8);
REGMBC(0x14c) REGMBC(0x14e) REGMBC(0x150)
REGMBC(0x1a0) REGMBC(0x1d1) REGMBC(0x1ea)
REGMBC(0x1ec) REGMBC(0x1ece)
@@ -907,12 +908,12 @@ static void reg_equi_class(int c)
regmbc('T'); REGMBC(0x162) REGMBC(0x164)
REGMBC(0x166) REGMBC(0x1e6a) REGMBC(0x1e6e)
return;
- case 'U': case '\331': case '\332': case '\333': case '\334':
+ case 'U': case 0xd9: case 0xda: case 0xdb: case 0xdc:
CASEMBC(0x168) CASEMBC(0x16a) CASEMBC(0x16c) CASEMBC(0x16e)
CASEMBC(0x170) CASEMBC(0x172) CASEMBC(0x1af) CASEMBC(0x1d3)
CASEMBC(0x1ee6)
- regmbc('U'); regmbc('\331'); regmbc('\332');
- regmbc('\333'); regmbc('\334');
+ regmbc('U'); regmbc(0xd9); regmbc(0xda);
+ regmbc(0xdb); regmbc(0xdc);
REGMBC(0x168) REGMBC(0x16a) REGMBC(0x16c)
REGMBC(0x16e) REGMBC(0x170) REGMBC(0x172)
REGMBC(0x1af) REGMBC(0x1d3) REGMBC(0x1ee6)
@@ -928,10 +929,10 @@ static void reg_equi_class(int c)
case 'X': CASEMBC(0x1e8a) CASEMBC(0x1e8c)
regmbc('X'); REGMBC(0x1e8a) REGMBC(0x1e8c)
return;
- case 'Y': case '\335':
+ case 'Y': case 0xdd:
CASEMBC(0x176) CASEMBC(0x178) CASEMBC(0x1e8e) CASEMBC(0x1ef2)
CASEMBC(0x1ef6) CASEMBC(0x1ef8)
- regmbc('Y'); regmbc('\335');
+ regmbc('Y'); regmbc(0xdd);
REGMBC(0x176) REGMBC(0x178) REGMBC(0x1e8e)
REGMBC(0x1ef2) REGMBC(0x1ef6) REGMBC(0x1ef8)
return;
@@ -941,13 +942,13 @@ static void reg_equi_class(int c)
REGMBC(0x17d) REGMBC(0x1b5) REGMBC(0x1e90)
REGMBC(0x1e94)
return;
- case 'a': case '\340': case '\341': case '\342':
- case '\343': case '\344': case '\345':
+ case 'a': case 0xe0: case 0xe1: case 0xe2:
+ case 0xe3: case 0xe4: case 0xe5:
CASEMBC(0x101) CASEMBC(0x103) CASEMBC(0x105) CASEMBC(0x1ce)
CASEMBC(0x1df) CASEMBC(0x1e1) CASEMBC(0x1ea3)
- regmbc('a'); regmbc('\340'); regmbc('\341');
- regmbc('\342'); regmbc('\343'); regmbc('\344');
- regmbc('\345');
+ regmbc('a'); regmbc(0xe0); regmbc(0xe1);
+ regmbc(0xe2); regmbc(0xe3); regmbc(0xe4);
+ regmbc(0xe5);
REGMBC(0x101) REGMBC(0x103) REGMBC(0x105)
REGMBC(0x1ce) REGMBC(0x1df) REGMBC(0x1e1)
REGMBC(0x1ea3)
@@ -955,9 +956,9 @@ static void reg_equi_class(int c)
case 'b': CASEMBC(0x1e03) CASEMBC(0x1e07)
regmbc('b'); REGMBC(0x1e03) REGMBC(0x1e07)
return;
- case 'c': case '\347':
+ case 'c': case 0xe7:
CASEMBC(0x107) CASEMBC(0x109) CASEMBC(0x10b) CASEMBC(0x10d)
- regmbc('c'); regmbc('\347');
+ regmbc('c'); regmbc(0xe7);
REGMBC(0x107) REGMBC(0x109) REGMBC(0x10b)
REGMBC(0x10d)
return;
@@ -966,11 +967,11 @@ static void reg_equi_class(int c)
regmbc('d'); REGMBC(0x10f) REGMBC(0x111)
REGMBC(0x1e0b) REGMBC(0x1e0f) REGMBC(0x1e11)
return;
- case 'e': case '\350': case '\351': case '\352': case '\353':
+ case 'e': case 0xe8: case 0xe9: case 0xea: case 0xeb:
CASEMBC(0x113) CASEMBC(0x115) CASEMBC(0x117) CASEMBC(0x119)
CASEMBC(0x11b) CASEMBC(0x1ebb) CASEMBC(0x1ebd)
- regmbc('e'); regmbc('\350'); regmbc('\351');
- regmbc('\352'); regmbc('\353');
+ regmbc('e'); regmbc(0xe8); regmbc(0xe9);
+ regmbc(0xea); regmbc(0xeb);
REGMBC(0x113) REGMBC(0x115) REGMBC(0x117)
REGMBC(0x119) REGMBC(0x11b) REGMBC(0x1ebb)
REGMBC(0x1ebd)
@@ -991,11 +992,11 @@ static void reg_equi_class(int c)
REGMBC(0x1e23) REGMBC(0x1e27) REGMBC(0x1e29)
REGMBC(0x1e96)
return;
- case 'i': case '\354': case '\355': case '\356': case '\357':
+ case 'i': case 0xec: case 0xed: case 0xee: case 0xef:
CASEMBC(0x129) CASEMBC(0x12b) CASEMBC(0x12d) CASEMBC(0x12f)
CASEMBC(0x1d0) CASEMBC(0x1ec9)
- regmbc('i'); regmbc('\354'); regmbc('\355');
- regmbc('\356'); regmbc('\357');
+ regmbc('i'); regmbc(0xec); regmbc(0xed);
+ regmbc(0xee); regmbc(0xef);
REGMBC(0x129) REGMBC(0x12b) REGMBC(0x12d)
REGMBC(0x12f) REGMBC(0x1d0) REGMBC(0x1ec9)
return;
@@ -1016,20 +1017,20 @@ static void reg_equi_class(int c)
case 'm': CASEMBC(0x1e3f) CASEMBC(0x1e41)
regmbc('m'); REGMBC(0x1e3f) REGMBC(0x1e41)
return;
- case 'n': case '\361':
+ case 'n': case 0xf1:
CASEMBC(0x144) CASEMBC(0x146) CASEMBC(0x148) CASEMBC(0x149)
CASEMBC(0x1e45) CASEMBC(0x1e49)
- regmbc('n'); regmbc('\361');
+ regmbc('n'); regmbc(0xf1);
REGMBC(0x144) REGMBC(0x146) REGMBC(0x148)
REGMBC(0x149) REGMBC(0x1e45) REGMBC(0x1e49)
return;
- case 'o': case '\362': case '\363': case '\364': case '\365':
- case '\366': case '\370':
+ case 'o': case 0xf2: case 0xf3: case 0xf4: case 0xf5:
+ case 0xf6: case 0xf8:
CASEMBC(0x14d) CASEMBC(0x14f) CASEMBC(0x151) CASEMBC(0x1a1)
CASEMBC(0x1d2) CASEMBC(0x1eb) CASEMBC(0x1ed) CASEMBC(0x1ecf)
- regmbc('o'); regmbc('\362'); regmbc('\363');
- regmbc('\364'); regmbc('\365'); regmbc('\366');
- regmbc('\370');
+ regmbc('o'); regmbc(0xf2); regmbc(0xf3);
+ regmbc(0xf4); regmbc(0xf5); regmbc(0xf6);
+ regmbc(0xf8);
REGMBC(0x14d) REGMBC(0x14f) REGMBC(0x151)
REGMBC(0x1a1) REGMBC(0x1d2) REGMBC(0x1eb)
REGMBC(0x1ed) REGMBC(0x1ecf)
@@ -1052,12 +1053,12 @@ static void reg_equi_class(int c)
regmbc('t'); REGMBC(0x163) REGMBC(0x165) REGMBC(0x167)
REGMBC(0x1e6b) REGMBC(0x1e6f) REGMBC(0x1e97)
return;
- case 'u': case '\371': case '\372': case '\373': case '\374':
+ case 'u': case 0xf9: case 0xfa: case 0xfb: case 0xfc:
CASEMBC(0x169) CASEMBC(0x16b) CASEMBC(0x16d) CASEMBC(0x16f)
CASEMBC(0x171) CASEMBC(0x173) CASEMBC(0x1b0) CASEMBC(0x1d4)
CASEMBC(0x1ee7)
- regmbc('u'); regmbc('\371'); regmbc('\372');
- regmbc('\373'); regmbc('\374');
+ regmbc('u'); regmbc(0xf9); regmbc(0xfa);
+ regmbc(0xfb); regmbc(0xfc);
REGMBC(0x169) REGMBC(0x16b) REGMBC(0x16d)
REGMBC(0x16f) REGMBC(0x171) REGMBC(0x173)
REGMBC(0x1b0) REGMBC(0x1d4) REGMBC(0x1ee7)
@@ -1074,10 +1075,10 @@ static void reg_equi_class(int c)
case 'x': CASEMBC(0x1e8b) CASEMBC(0x1e8d)
regmbc('x'); REGMBC(0x1e8b) REGMBC(0x1e8d)
return;
- case 'y': case '\375': case '\377':
+ case 'y': case 0xfd: case 0xff:
CASEMBC(0x177) CASEMBC(0x1e8f) CASEMBC(0x1e99)
CASEMBC(0x1ef3) CASEMBC(0x1ef7) CASEMBC(0x1ef9)
- regmbc('y'); regmbc('\375'); regmbc('\377');
+ regmbc('y'); regmbc(0xfd); regmbc(0xff);
REGMBC(0x177) REGMBC(0x1e8f) REGMBC(0x1e99)
REGMBC(0x1ef3) REGMBC(0x1ef7) REGMBC(0x1ef9)
return;
@@ -3444,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. */
@@ -4120,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 */
@@ -4186,7 +4188,7 @@ regmatch (
/* When only a composing char is given match at any
* position where that composing char appears. */
status = RA_NOMATCH;
- for (i = 0; reginput[i] != NUL; i += utf_char2len(inpc)) {
+ for (i = 0; reginput[i] != NUL; i += utf_ptr2len(reginput + i)) {
inpc = mb_ptr2char(reginput + i);
if (!utf_iscomposing(inpc)) {
if (i > 0)
@@ -5333,10 +5335,12 @@ do_class:
if ((len = (*mb_ptr2len)(opnd)) > 1) {
if (ireg_ic && enc_utf8)
cf = utf_fold(utf_ptr2char(opnd));
- while (count < maxcount) {
- for (i = 0; i < len; ++i)
- if (opnd[i] != scan[i])
+ while (count < maxcount && (*mb_ptr2len)(scan) >= len) {
+ for (i = 0; i < len; ++i) {
+ if (opnd[i] != scan[i]) {
break;
+ }
+ }
if (i < len && (!ireg_ic || !enc_utf8
|| utf_fold(utf_ptr2char(scan)) != cf))
break;
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index b96dcc66b3..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(_(
@@ -4762,66 +4762,71 @@ static int skip_to_start(int c, colnr_T *colp)
*/
static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
{
- colnr_T col = startcol;
- int c1, c2;
- int len1, len2;
- int match;
+#define PTR2LEN(x) enc_utf8 ? utf_ptr2len(x) : MB_PTR2LEN(x)
- for (;; ) {
- match = TRUE;
- len2 = MB_CHAR2LEN(regstart); /* skip regstart */
- for (len1 = 0; match_text[len1] != NUL; len1 += MB_CHAR2LEN(c1)) {
- c1 = PTR2CHAR(match_text + len1);
- c2 = PTR2CHAR(regline + col + len2);
- if (c1 != c2 && (!ireg_ic || vim_tolower(c1) != vim_tolower(c2))) {
- match = FALSE;
+ colnr_T col = startcol;
+ int regstart_len = PTR2LEN(regline + startcol);
+
+ for (;;) {
+ bool match = true;
+ char_u *s1 = match_text;
+ char_u *s2 = regline + col + regstart_len; // skip regstart
+ while (*s1) {
+ int c1_len = PTR2LEN(s1);
+ int c1 = PTR2CHAR(s1);
+ int c2_len = PTR2LEN(s2);
+ int c2 = PTR2CHAR(s2);
+
+ if ((c1 != c2 && (!ireg_ic || vim_tolower(c1) != vim_tolower(c2)))
+ || c1_len != c2_len) {
+ match = false;
break;
}
- len2 += MB_CHAR2LEN(c2);
+ s1 += c1_len;
+ s2 += c2_len;
}
if (match
- /* check that no composing char follows */
- && !(enc_utf8
- && STRLEN(regline) > (size_t)(col + len2)
- && utf_iscomposing(PTR2CHAR(regline + col + len2)))
- ) {
+ // check that no composing char follows
+ && !(enc_utf8 && utf_iscomposing(PTR2CHAR(s2)))) {
cleanup_subexpr();
if (REG_MULTI) {
reg_startpos[0].lnum = reglnum;
reg_startpos[0].col = col;
reg_endpos[0].lnum = reglnum;
- reg_endpos[0].col = col + len2;
+ reg_endpos[0].col = s2 - regline;
} else {
reg_startp[0] = regline + col;
- reg_endp[0] = regline + col + len2;
+ reg_endp[0] = s2;
}
return 1L;
}
- /* Try finding regstart after the current match. */
- col += MB_CHAR2LEN(regstart); /* skip regstart */
- if (skip_to_start(regstart, &col) == FAIL)
+ // Try finding regstart after the current match.
+ col += regstart_len; // skip regstart
+ if (skip_to_start(regstart, &col) == FAIL) {
break;
+ }
}
return 0L;
+
+#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;
@@ -4838,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;
@@ -4917,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 */
@@ -5000,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);
@@ -5065,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:
@@ -5085,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
@@ -5094,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;
}
@@ -5137,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;
@@ -5153,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);
}
@@ -5169,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
@@ -5199,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;
@@ -5219,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;
@@ -5272,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;
@@ -5296,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;
@@ -5346,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;
@@ -5378,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)
@@ -5395,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;
}
@@ -5403,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;
}
@@ -5418,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);
@@ -5438,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",
@@ -5471,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;
}
@@ -5480,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;
@@ -5499,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);
@@ -5673,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;
@@ -5688,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;
@@ -5708,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;
@@ -5766,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;
@@ -5796,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
@@ -5855,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;
@@ -5875,14 +5888,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
// If ireg_icombine is not set only skip over the character
// itself. When it is set skip over composing characters.
if (result && enc_utf8 && !ireg_icombine) {
- clen = utf_char2len(curc);
+ clen = utf_ptr2len(reginput);
}
ADD_STATE_IF_MATCH(t->state);
break;
}
-
- } /* switch (t->state->c) */
+ } // switch (t->state->c)
if (add_state != NULL) {
nfa_pim_T *pim;
@@ -5893,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
@@ -5906,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);
@@ -5927,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;
@@ -5968,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
@@ -5995,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;
@@ -6005,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))) {
@@ -6053,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
@@ -6077,7 +6098,7 @@ nextchar:
#endif
theend:
- /* Free memory */
+ // Free memory
xfree(list[0].t);
xfree(list[1].t);
xfree(listids);
@@ -6089,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;
@@ -6103,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");
@@ -6127,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;
@@ -6173,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];
@@ -6199,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;
@@ -6289,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;
@@ -6441,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.
@@ -6492,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 9fdb476748..10b5b6bba4 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -147,6 +147,9 @@ static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */
*/
static schar_T *current_ScreenLine;
+StlClickDefinition *tab_page_click_defs = NULL;
+long tab_page_click_defs_size = 0;
+
# define SCREEN_LINE(r, o, e, c, rl) screen_line((r), (o), (e), (c), (rl))
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.c.generated.h"
@@ -384,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);
+ }
}
}
@@ -752,10 +756,11 @@ static void win_update(win_T *wp)
lnumt = wp->w_lines[i].wl_lastlnum + 1;
if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot) {
lnumb = wp->w_lines[i].wl_lnum;
- /* When there is a fold column it might need updating
- * in the next line ("J" just above an open fold). */
- if (wp->w_p_fdc > 0)
- ++lnumb;
+ // When there is a fold column it might need updating
+ // in the next line ("J" just above an open fold).
+ if (compute_foldcolumn(wp, 0) > 0) {
+ lnumb++;
+ }
}
}
@@ -1227,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
@@ -1567,10 +1572,11 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h
{
int n = 0;
# define FDC_OFF n
+ int fdc = compute_foldcolumn(wp, 0);
if (wp->w_p_rl) {
- /* No check for cmdline window: should never be right-left. */
- n = wp->w_p_fdc;
+ // No check for cmdline window: should never be right-left.
+ n = fdc;
if (n > 0) {
/* draw the fold column at the right */
@@ -1610,8 +1616,8 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h
wp->w_wincol, wp->w_wincol + n,
cmdwin_type, ' ', hl_attr(HLF_AT));
}
- if (wp->w_p_fdc > 0) {
- int nn = n + wp->w_p_fdc;
+ if (fdc > 0) {
+ int nn = n + fdc;
/* draw the fold column at the left */
if (nn > wp->w_width)
@@ -1654,6 +1660,20 @@ static int advance_color_col(int vcol, int **color_cols)
return **color_cols >= 0;
}
+// Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
+// space is available for window "wp", minus "col".
+static int compute_foldcolumn(win_T *wp, int col)
+{
+ int fdc = wp->w_p_fdc;
+ int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw;
+ int wwidth = wp->w_width;
+
+ if (fdc > wwidth - (col + wmw)) {
+ fdc = wwidth - (col + wmw);
+ }
+ return fdc;
+}
+
/*
* Display one folded line.
*/
@@ -1692,12 +1712,9 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
++col;
}
- /*
- * 2. Add the 'foldcolumn'
- */
- fdc = wp->w_p_fdc;
- if (fdc > wp->w_width - col)
- fdc = wp->w_width - col;
+ // 2. Add the 'foldcolumn'
+ // Reduce the width when there is not enough space.
+ fdc = compute_foldcolumn(wp, col);
if (fdc > 0) {
fill_foldcolumn(buf, wp, TRUE, lnum);
if (wp->w_p_rl) {
@@ -2018,37 +2035,42 @@ fill_foldcolumn (
int level;
int first_level;
int empty;
+ int fdc = compute_foldcolumn(wp, 0);
- /* Init to all spaces. */
- memset(p, ' ', (size_t)wp->w_p_fdc);
+ // Init to all spaces.
+ memset(p, ' ', (size_t)fdc);
level = win_foldinfo.fi_level;
if (level > 0) {
- /* If there is only one column put more info in it. */
- empty = (wp->w_p_fdc == 1) ? 0 : 1;
+ // If there is only one column put more info in it.
+ empty = (fdc == 1) ? 0 : 1;
- /* If the column is too narrow, we start at the lowest level that
- * fits and use numbers to indicated the depth. */
- first_level = level - wp->w_p_fdc - closed + 1 + empty;
- if (first_level < 1)
+ // If the column is too narrow, we start at the lowest level that
+ // fits and use numbers to indicated the depth.
+ first_level = level - fdc - closed + 1 + empty;
+ if (first_level < 1) {
first_level = 1;
+ }
- for (i = 0; i + empty < wp->w_p_fdc; ++i) {
+ for (i = 0; i + empty < fdc; i++) {
if (win_foldinfo.fi_lnum == lnum
- && first_level + i >= win_foldinfo.fi_low_level)
+ && first_level + i >= win_foldinfo.fi_low_level) {
p[i] = '-';
- else if (first_level == 1)
+ } else if (first_level == 1) {
p[i] = '|';
- else if (first_level + i <= 9)
+ } else if (first_level + i <= 9) {
p[i] = '0' + first_level + i;
- else
+ } else {
p[i] = '>';
- if (first_level + i == level)
+ }
+ if (first_level + i == level) {
break;
+ }
}
}
- if (closed)
- p[i >= wp->w_p_fdc ? i - 1 : i] = '+';
+ if (closed) {
+ p[i >= fdc ? i - 1 : i] = '+';
+ }
}
/*
@@ -2163,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 */
@@ -2178,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 \
@@ -2221,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)
@@ -2409,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);
+ }
}
/*
@@ -2435,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;
}
@@ -2612,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;
@@ -2629,11 +2661,13 @@ win_line (
}
if (draw_state == WL_FOLD - 1 && n_extra == 0) {
+ int fdc = compute_foldcolumn(wp, 0);
+
draw_state = WL_FOLD;
- if (wp->w_p_fdc > 0) {
- /* Draw the 'foldcolumn'. */
- fill_foldcolumn(extra, wp, FALSE, lnum);
- n_extra = wp->w_p_fdc;
+ if (fdc > 0) {
+ // Draw the 'foldcolumn'.
+ fill_foldcolumn(extra, wp, false, lnum);
+ n_extra = fdc;
p_extra = extra;
p_extra[n_extra] = NUL;
c_extra = NUL;
@@ -2861,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);
@@ -2934,14 +2976,16 @@ win_line (
}
}
- /* Decide which of the highlight attributes to use. */
- attr_pri = TRUE;
- if (area_attr != 0)
- char_attr = area_attr;
- else if (search_attr != 0)
- char_attr = search_attr;
- /* Use line_attr when not in the Visual or 'incsearch' area
- * (area_attr may be 0 when "noinvcur" is set). */
+ // Decide which of the highlight attributes to use.
+ attr_pri = true;
+
+ if (area_attr != 0) {
+ char_attr = hl_combine_attr(line_attr, area_attr);
+ } else if (search_attr != 0) {
+ char_attr = hl_combine_attr(line_attr, search_attr);
+ }
+ // Use line_attr when not in the Visual or 'incsearch' area
+ // (area_attr may be 0 when "noinvcur" is set).
else if (line_attr != 0 && ((fromcol == -10 && tocol == MAXCOL)
|| vcol < fromcol || vcol_prev < fromcol_prev
|| vcol >= tocol))
@@ -3176,26 +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)) && 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;
@@ -3244,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) {
@@ -3315,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]);
}
@@ -3323,16 +3359,15 @@ win_line (
* Found last space before word: check for line break.
*/
if (wp->w_p_lbr && vim_isbreak(c) && !vim_isbreak(*ptr)) {
- char_u *p = ptr - (
- has_mbyte ? mb_l :
- 1);
+ int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0;
+ char_u *p = ptr - (mb_off + 1);
// TODO: is passing p for start of the line OK?
n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1;
if (c == TAB && n_extra + col > wp->w_width) {
n_extra = (int)wp->w_buffer->b_p_ts
- vcol % (int)wp->w_buffer->b_p_ts - 1;
}
- c_extra = ' ';
+ c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
if (ascii_iswhite(c)) {
if (c == TAB)
/* See "Tab alignment" below. */
@@ -3343,6 +3378,28 @@ 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;
+ 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;
+ }
+ }
+
if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') {
c = lcs_trail;
if (!attr_pri) {
@@ -3388,7 +3445,7 @@ win_line (
int i;
int saved_nextra = n_extra;
- if ((is_concealing || boguscols > 0) && vcol_off > 0) {
+ if (vcol_off > 0) {
// there are characters to conceal
tab_len += vcol_off;
}
@@ -3418,25 +3475,31 @@ win_line (
// n_extra will be increased by FIX_FOX_BOGUSCOLS
// macro below, so need to adjust for that here
- if ((is_concealing || boguscols > 0) && vcol_off > 0) {
+ if (vcol_off > 0) {
n_extra -= vcol_off;
}
}
- /* Tab alignment should be identical regardless of
- * 'conceallevel' value. So tab compensates of all
- * previous concealed characters, and thus resets vcol_off
- * and boguscols accumulated so far in the line. Note that
- * the tab can be longer than 'tabstop' when there
- * are concealed characters. */
- FIX_FOR_BOGUSCOLS;
- // Make sure that the highlighting for the tab char will be correctly
- // set further below (effectively reverts the FIX_FOR_BOGSUCOLS
- // macro).
- if (old_boguscols > 0 && n_extra > tab_len && wp->w_p_list
- && lcs_tab1) {
- tab_len += n_extra - tab_len;
+
+ {
+ int vc_saved = vcol_off;
+
+ // Tab alignment should be identical regardless of
+ // 'conceallevel' value. So tab compensates of all
+ // previous concealed characters, and thus resets
+ // vcol_off and boguscols accumulated so far in the
+ // line. Note that the tab can be longer than
+ // 'tabstop' when there are concealed characters.
+ FIX_FOR_BOGUSCOLS;
+
+ // Make sure, the highlighting for the tab char will be
+ // correctly set further below (effectively reverts the
+ // FIX_FOR_BOGSUCOLS macro.
+ if (n_extra == tab_len + vc_saved && wp->w_p_list && lcs_tab1) {
+ tab_len += vc_saved;
+ }
}
- mb_utf8 = FALSE; /* don't draw as UTF-8 */
+
+ mb_utf8 = (int)false; // don't draw as UTF-8
if (wp->w_p_list) {
c = lcs_tab1;
if (wp->w_p_lbr) {
@@ -3458,7 +3521,7 @@ win_line (
c = ' ';
}
} else if (c == NUL
- && ((wp->w_p_list && lcs_eol > 0)
+ && (wp->w_p_list
|| ((fromcol >= 0 || fromcol_prev >= 0)
&& tocol > vcol
&& VIsual_mode != Ctrl_V
@@ -3468,11 +3531,11 @@ win_line (
&& !(noinvcur
&& lnum == wp->w_cursor.lnum
&& (colnr_T)vcol == wp->w_virtcol)))
- && lcs_eol_one >= 0) {
- /* Display a '$' after the line or highlight an extra
- * character if the line break is included. */
- /* For a diff line the highlighting continues after the
- * "$". */
+ && lcs_eol_one > 0) {
+ // Display a '$' after the line or highlight an extra
+ // character if the line break is included.
+ // For a diff line the highlighting continues after the
+ // "$".
if (diff_hlf == (hlf_T)0 && line_attr == 0) {
/* In virtualedit, visual selections may extend
* beyond end of line. */
@@ -3485,10 +3548,11 @@ win_line (
c_extra = NUL;
}
}
- if (wp->w_p_list)
+ if (wp->w_p_list && lcs_eol > 0) {
c = lcs_eol;
- else
+ } else {
c = ' ';
+ }
lcs_eol_one = -1;
--ptr; /* put it back at the NUL */
if (!attr_pri) {
@@ -3539,25 +3603,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) {
@@ -3570,24 +3631,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;
@@ -3628,9 +3693,13 @@ 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. */
@@ -3946,20 +4015,24 @@ win_line (
ScreenAttrs[off] = char_attr;
if (has_mbyte && (*mb_char2cells)(mb_c) > 1) {
- /* Need to fill two screen columns. */
- ++off;
- ++col;
- if (enc_utf8)
- /* UTF-8: Put a 0 in the second screen char. */
+ // Need to fill two screen columns.
+ off++;
+ col++;
+ if (enc_utf8) {
+ // UTF-8: Put a 0 in the second screen char.
ScreenLines[off] = 0;
- else
- /* DBCS: Put second byte in the second screen char. */
+ } else {
+ // DBCS: Put second byte in the second screen char.
ScreenLines[off] = mb_c & 0xff;
- ++vcol;
- /* When "tocol" is halfway through a character, set it to the end of
- * the character, otherwise highlighting won't stop. */
- if (tocol == vcol)
- ++tocol;
+ }
+ if (draw_state > WL_NR && filler_todo <= 0) {
+ vcol++;
+ }
+ // When "tocol" is halfway through a character, set it to the end of
+ // the character, otherwise highlighting won't stop.
+ if (tocol == vcol) {
+ tocol++;
+ }
if (wp->w_p_rl) {
/* now it's time to backup one cell */
--off;
@@ -4104,7 +4177,6 @@ win_line (
* xterm/screen: write an extra character beyond the end of
* the line. This will work with all terminal types
* (regardless of the xn,am settings).
- * Only do this on a fast tty.
* Only do this if the cursor is on the current line
* (something has been written in it).
* Don't do this for the GUI.
@@ -4977,8 +5049,8 @@ win_redr_custom (
char_u *stl;
char_u *p;
struct stl_hlrec hltab[STL_MAX_ITEM];
- struct stl_hlrec tabtab[STL_MAX_ITEM];
- int use_sandbox = FALSE;
+ StlClickRecord tabtab[STL_MAX_ITEM];
+ int use_sandbox = false;
win_T *ewp;
int p_crb_save;
@@ -5094,20 +5166,24 @@ win_redr_custom (
screen_puts(p >= buf + len ? (char_u *)"" : p, row, col, curattr);
if (wp == NULL) {
- /* Fill the TabPageIdxs[] array for clicking in the tab pagesline. */
+ // Fill the tab_page_click_defs array for clicking in the tab pages line.
col = 0;
len = 0;
p = buf;
- fillchar = 0;
+ StlClickDefinition cur_click_def = {
+ .type = kStlClickDisabled,
+ };
for (n = 0; tabtab[n].start != NULL; n++) {
- len += vim_strnsize(p, (int)(tabtab[n].start - p));
- while (col < len)
- TabPageIdxs[col++] = fillchar;
- p = tabtab[n].start;
- fillchar = tabtab[n].userhl;
+ len += vim_strnsize(p, (int)(tabtab[n].start - (char *) p));
+ while (col < len) {
+ tab_page_click_defs[col++] = cur_click_def;
+ }
+ p = (char_u *) tabtab[n].start;
+ cur_click_def = tabtab[n].def;
+ }
+ while (col < Columns) {
+ tab_page_click_defs[col++] = cur_click_def;
}
- while (col < Columns)
- TabPageIdxs[col++] = fillchar;
}
theend:
@@ -5926,9 +6002,9 @@ void screenalloc(bool doclear)
sattr_T *new_ScreenAttrs;
unsigned *new_LineOffset;
char_u *new_LineWraps;
- short *new_TabPageIdxs;
- static int entered = FALSE; /* avoid recursiveness */
- static int done_outofmem_msg = FALSE; /* did outofmem message */
+ StlClickDefinition *new_tab_page_click_defs;
+ static bool entered = false; // avoid recursiveness
+ static bool done_outofmem_msg = false;
int retry_count = 0;
const bool l_enc_utf8 = enc_utf8;
const int l_enc_dbcs = enc_dbcs;
@@ -6001,7 +6077,8 @@ retry:
new_ScreenAttrs = xmalloc((size_t)((Rows + 1) * Columns * sizeof(sattr_T)));
new_LineOffset = xmalloc((size_t)(Rows * sizeof(unsigned)));
new_LineWraps = xmalloc((size_t)(Rows * sizeof(char_u)));
- new_TabPageIdxs = xmalloc((size_t)(Columns * sizeof(short)));
+ new_tab_page_click_defs = xcalloc(
+ (size_t) Columns, sizeof(*new_tab_page_click_defs));
FOR_ALL_TAB_WINDOWS(tp, wp) {
win_alloc_lines(wp);
@@ -6019,7 +6096,7 @@ retry:
|| new_ScreenAttrs == NULL
|| new_LineOffset == NULL
|| new_LineWraps == NULL
- || new_TabPageIdxs == NULL
+ || new_tab_page_click_defs == NULL
|| outofmem) {
if (ScreenLines != NULL || !done_outofmem_msg) {
/* guess the size */
@@ -6045,8 +6122,8 @@ retry:
new_LineOffset = NULL;
xfree(new_LineWraps);
new_LineWraps = NULL;
- xfree(new_TabPageIdxs);
- new_TabPageIdxs = NULL;
+ xfree(new_tab_page_click_defs);
+ new_tab_page_click_defs = NULL;
} else {
done_outofmem_msg = FALSE;
@@ -6125,7 +6202,8 @@ retry:
ScreenAttrs = new_ScreenAttrs;
LineOffset = new_LineOffset;
LineWraps = new_LineWraps;
- TabPageIdxs = new_TabPageIdxs;
+ tab_page_click_defs = new_tab_page_click_defs;
+ tab_page_click_defs_size = Columns;
/* It's important that screen_Rows and screen_Columns reflect the actual
* size of ScreenLines[]. Set them before calling anything. */
@@ -6164,7 +6242,25 @@ void free_screenlines(void)
xfree(ScreenAttrs);
xfree(LineOffset);
xfree(LineWraps);
- xfree(TabPageIdxs);
+ clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size);
+ xfree(tab_page_click_defs);
+}
+
+/// Clear tab_page_click_defs table
+///
+/// @param[out] tpcd Table to clear.
+/// @param[in] tpcd_size Size of the table.
+void clear_tab_page_click_defs(StlClickDefinition *const tpcd,
+ const long tpcd_size)
+{
+ if (tpcd != NULL) {
+ for (long i = 0; i < tpcd_size; i++) {
+ if (i == 0 || tpcd[i].func != tpcd[i - 1].func) {
+ xfree(tpcd[i].func);
+ }
+ }
+ memset(tpcd, 0, (size_t) tpcd_size * sizeof(tpcd[0]));
+ }
}
void screenclear(void)
@@ -6681,8 +6777,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;
@@ -6721,26 +6817,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.
*/
@@ -6772,9 +6875,9 @@ static void draw_tabline(void)
return;
- /* Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. */
- for (scol = 0; scol < Columns; ++scol)
- TabPageIdxs[scol] = 0;
+ // Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect.
+ assert(Columns == tab_page_click_defs_size);
+ clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size);
/* Use the 'tabline' option if it's set. */
if (*p_tal != NUL) {
@@ -6872,11 +6975,16 @@ static void draw_tabline(void)
}
screen_putchar(' ', 0, col++, attr);
- /* Store the tab page number in TabPageIdxs[], so that
- * jump_to_mouse() knows where each one is. */
- ++tabcount;
- while (scol < col)
- TabPageIdxs[scol++] = tabcount;
+ // Store the tab page number in tab_page_click_defs[], so that
+ // jump_to_mouse() knows where each one is.
+ tabcount++;
+ while (scol < col) {
+ tab_page_click_defs[scol++] = (StlClickDefinition) {
+ .type = kStlClickTabSwitch,
+ .tabnr = tabcount,
+ .func = NULL,
+ };
+ }
}
if (use_sep_chars)
@@ -6888,7 +6996,11 @@ static void draw_tabline(void)
/* Put an "X" for closing the current tab if there are several. */
if (first_tabpage->tp_next != NULL) {
screen_putchar('X', 0, (int)Columns - 1, attr_nosel);
- TabPageIdxs[Columns - 1] = -999;
+ tab_page_click_defs[Columns - 1] = (StlClickDefinition) {
+ .type = kStlClickTabClose,
+ .tabnr = 999,
+ .func = NULL,
+ };
}
}
diff --git a/src/nvim/screen.h b/src/nvim/screen.h
index debf86ae67..81a8b9ed4c 100644
--- a/src/nvim/screen.h
+++ b/src/nvim/screen.h
@@ -16,6 +16,29 @@
#define NOT_VALID 40 /* buffer needs complete redraw */
#define CLEAR 50 /* screen messed up, clear it */
+/// Status line click definition
+typedef struct {
+ enum {
+ kStlClickDisabled = 0, ///< Clicks to this area are ignored.
+ kStlClickTabSwitch, ///< Switch to the given tab.
+ kStlClickTabClose, ///< Close given tab.
+ kStlClickFuncRun, ///< Run user function.
+ } type; ///< Type of the click.
+ int tabnr; ///< Tab page number.
+ char *func; ///< Function to run.
+} StlClickDefinition;
+
+/// Used for tabline clicks
+typedef struct {
+ StlClickDefinition def; ///< Click definition.
+ const char *start; ///< Location where region starts.
+} StlClickRecord;
+
+/// Array defining what should be done when tabline is clicked
+extern StlClickDefinition *tab_page_click_defs;
+
+/// Size of the tab_page_click_defs array
+extern long tab_page_click_defs_size;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.h.generated.h"
diff --git a/src/nvim/search.c b/src/nvim/search.c
index cb461c9ef2..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];
@@ -621,43 +624,39 @@ int searchit(
break;
}
matchcol = endpos.col;
- /* for empty match: advance one char */
- if (matchcol == matchpos.col
- && ptr[matchcol] != NUL) {
- if (has_mbyte)
- matchcol +=
- (*mb_ptr2len)(ptr + matchcol);
- else
- ++matchcol;
- }
+ // for empty match (matchcol == matchpos.col): advance one char
} else {
+ // Prepare to start after first matched character.
matchcol = matchpos.col;
- if (ptr[matchcol] != NUL) {
- if (has_mbyte)
- matchcol += (*mb_ptr2len)(ptr
- + matchcol);
- else
- ++matchcol;
- }
}
- if (matchcol == 0 && (options & SEARCH_START))
+
+ if (matchcol == matchpos.col && ptr[matchcol] != NUL) {
+ matchcol += MB_PTR2LEN(ptr + matchcol);
+ }
+
+ if (matchcol == 0 && (options & SEARCH_START)) {
break;
- if (STRLEN(ptr) <= (size_t)matchcol || ptr[matchcol] == NUL
- || (nmatched = vim_regexec_multi(&regmatch,
- win, buf, lnum + matchpos.lnum,
- matchcol,
- tm
- )) == 0) {
- match_ok = FALSE;
+ }
+
+ if (ptr[matchcol] == NUL
+ || (nmatched = vim_regexec_multi(&regmatch, win, buf, lnum,
+ matchcol, tm)) == 0) {
+ match_ok = false;
break;
}
matchpos = regmatch.startpos[0];
endpos = regmatch.endpos[0];
submatch = first_submatch(&regmatch);
- /* Need to get the line pointer again, a
- * multi-line search may have made it invalid. */
- ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE);
+ // This while-loop only works with matchpos.lnum == 0.
+ // For bigger values the next line pointer ptr might not be a
+ // buffer line.
+ if (matchpos.lnum != 0) {
+ break;
+ }
+ // Need to get the line pointer again, a multi-line search may
+ // have made it invalid.
+ ptr = ml_get_buf(buf, lnum, false);
}
if (!match_ok)
continue;
@@ -683,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);
@@ -881,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;
@@ -918,7 +914,7 @@ static int first_submatch(regmmatch_T *rp)
* Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this
* makes the movement linewise without moving the match position.
*
- * return 0 for failure, 1 for found, 2 for found and line offset added
+ * Return 0 for failure, 1 for found, 2 for found and line offset added.
*/
int do_search(
oparg_T *oap, /* can be NULL */
@@ -1031,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' */
@@ -1443,19 +1438,64 @@ static int check_prevcol(char_u *linep, int col, int ch, int *prevcol)
}
/*
+ * Raw string start is found at linep[startpos.col - 1].
+ * Return true if the matching end can be found between startpos and endpos.
+ */
+static int find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos)
+{
+ char_u *p;
+ char_u *delim_copy;
+ size_t delim_len;
+ linenr_T lnum;
+ int found = false;
+
+ for (p = linep + startpos->col + 1; *p && *p != '('; ++p) {}
+
+ delim_len = (p - linep) - startpos->col - 1;
+ delim_copy = vim_strnsave(linep + startpos->col + 1, delim_len);
+ if (delim_copy == NULL)
+ return false;
+ for (lnum = startpos->lnum; lnum <= endpos->lnum; ++lnum)
+ {
+ char_u *line = ml_get(lnum);
+
+ for (p = line + (lnum == startpos->lnum
+ ? startpos->col + 1 : 0); *p; ++p)
+ {
+ if (lnum == endpos->lnum && (colnr_T)(p - line) >= endpos->col)
+ break;
+ if (*p == ')' && p[delim_len + 1] == '"'
+ && STRNCMP(delim_copy, p + 1, delim_len) == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ xfree(delim_copy);
+ return found;
+}
+
+/*
* findmatchlimit -- find the matching paren or brace, if it exists within
- * maxtravel lines of here. A maxtravel of 0 means search until falling off
- * the edge of the file.
+ * maxtravel lines of the cursor. A maxtravel of 0 means search until falling
+ * off the edge of the file.
*
* "initc" is the character to find a match for. NUL means to find the
- * character at or after the cursor.
+ * character at or after the cursor. Special values:
+ * '*' look for C-style comment / *
+ * '/' look for C-style comment / *, ignoring comment-end
+ * '#' look for preprocessor directives
+ * 'R' look for raw string start: R"delim(text)delim" (only backwards)
*
* flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#')
* FM_FORWARD search forwards (when initc is '/', '*' or '#')
* FM_BLOCKSTOP stop at start/end of block ({ or } in column 0)
* FM_SKIPCOMM skip comments (not implemented yet!)
*
- * "oap" is only used to set oap->motion_type for a linewise motion, it be
+ * "oap" is only used to set oap->motion_type for a linewise motion, it can be
* NULL
*/
@@ -1465,8 +1505,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
int findc = 0; /* matching brace */
int c;
int count = 0; /* cumulative number of braces */
- int backwards = FALSE; /* init for gcc */
- int inquote = FALSE; /* TRUE when inside quotes */
+ int backwards = false; /* init for gcc */
+ int raw_string = false; /* search for raw string */
+ int inquote = false; /* true when inside quotes */
char_u *linep; /* pointer to current line */
char_u *ptr;
int do_quotes; /* check for quotes in current line */
@@ -1506,22 +1547,22 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
* When '/' is used, we ignore running backwards into a star-slash, for
* "[*" command, we just want to find any comment.
*/
- if (initc == '/' || initc == '*') {
+ if (initc == '/' || initc == '*' || initc == 'R') {
comment_dir = dir;
if (initc == '/')
- ignore_cend = TRUE;
- backwards = (dir == FORWARD) ? FALSE : TRUE;
+ ignore_cend = true;
+ backwards = (dir == FORWARD) ? false : true;
+ raw_string = (initc == 'R');
initc = NUL;
} else if (initc != '#' && initc != NUL) {
find_mps_values(&initc, &findc, &backwards, TRUE);
if (findc == NUL)
return NULL;
- }
- /*
- * Either initc is '#', or no initc was given and we need to look under the
- * cursor.
- */
- else {
+ } else {
+ /*
+ * Either initc is '#', or no initc was given and we need to look
+ * under the cursor.
+ */
if (initc == '#') {
hash_dir = dir;
} else {
@@ -1605,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)
@@ -1741,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) {
@@ -1766,7 +1807,26 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
*/
if (pos.col == 0)
continue;
- else if ( linep[pos.col - 1] == '/'
+ else if (raw_string)
+ {
+ if (linep[pos.col - 1] == 'R'
+ && linep[pos.col] == '"'
+ && vim_strchr(linep + pos.col + 1, '(') != NULL)
+ {
+ /* Possible start of raw string. Now that we have the
+ * delimiter we can check if it ends before where we
+ * started searching, or before the previously found
+ * raw string start. */
+ if (!find_rawstring_end(linep, &pos,
+ count > 0 ? &match_pos : &curwin->w_cursor))
+ {
+ count++;
+ match_pos = pos;
+ match_pos.col--;
+ }
+ linep = ml_get(pos.lnum); /* may have been released */
+ }
+ } else if ( linep[pos.col - 1] == '/'
&& linep[pos.col] == '*'
&& (int)pos.col < comment_col) {
count++;
@@ -1898,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] == '\'') {
@@ -2123,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)
@@ -2310,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;
}
/*
@@ -2732,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;
}
@@ -2967,7 +3031,7 @@ extend:
else
oap->inclusive = false;
oap->start = start_pos;
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
}
return OK;
}
@@ -3015,18 +3079,18 @@ current_block (
} else
old_end = VIsual;
- /*
- * Search backwards for unclosed '(', '{', etc..
- * Put this position in start_pos.
- * Ignore quotes here.
- */
+ // Search backwards for unclosed '(', '{', etc..
+ // Put this position in start_pos.
+ // Ignore quotes here. Keep the "M" flag in 'cpo', as that is what the
+ // user wants.
save_cpo = p_cpo;
- p_cpo = (char_u *)"%";
+ p_cpo = (char_u *)(vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%");
while (count-- > 0) {
- if ((pos = findmatch(NULL, what)) == NULL)
+ if ((pos = findmatch(NULL, what)) == NULL) {
break;
+ }
curwin->w_cursor = *pos;
- start_pos = *pos; /* the findmatch for end_pos will overwrite *pos */
+ start_pos = *pos; // the findmatch for end_pos will overwrite *pos
}
p_cpo = save_cpo;
@@ -3079,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);
@@ -3193,6 +3259,7 @@ current_tagblock (
int do_include = include;
bool save_p_ws = p_ws;
int retval = FAIL;
+ int is_inclusive = true;
p_ws = false;
@@ -3287,9 +3354,16 @@ again:
if (inc_cursor() < 0)
break;
} else {
- /* Exclude the '<' of the end tag. */
- if (*get_cursor_pos_ptr() == '<')
+ char_u *c = get_cursor_pos_ptr();
+ // Exclude the '<' of the end tag.
+ // If the closing tag is on new line, do not decrement cursor, but make
+ // operation exclusive, so that the linefeed will be selected
+ if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0) {
+ // do not decrement cursor
+ is_inclusive = false;
+ } else if (*c == '<') {
dec_cursor();
+ }
}
end_pos = curwin->w_cursor;
@@ -3328,14 +3402,15 @@ 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. */
curwin->w_cursor = start_pos;
oap->inclusive = false;
- } else
- oap->inclusive = true;
+ } else {
+ oap->inclusive = is_inclusive;
+ }
}
retval = OK;
@@ -3492,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;
@@ -3734,7 +3809,7 @@ current_quote (
}
} else {
oap->start = curwin->w_cursor;
- oap->motion_type = MCHAR;
+ oap->motion_type = kMTCharWise;
}
/* Set end position. */
@@ -4078,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;
@@ -4105,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;
@@ -4272,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 340c14066a..51c8597d53 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1,8 +1,3 @@
-#ifdef HAVE_BE64TOH
-# define _BSD_SOURCE 1
-# define _DEFAULT_SOURCE 1
-# include <endian.h>
-#endif
#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
@@ -10,9 +5,7 @@
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
+#include <fcntl.h>
#include <assert.h>
#include <msgpack.h>
@@ -38,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"
@@ -46,9 +38,17 @@
#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_INCLUDE_FILE
+#endif
+
// Note: when using bufset hash pointers are intentionally casted to uintptr_t
// and not to khint32_t or khint64_t: this way compiler must give a warning
// (-Wconversion) when types change.
@@ -66,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) \
@@ -122,7 +119,7 @@ KHASH_SET_INIT_STR(strset)
// E576: Missing '>'
// E577: Illegal register name
// E886: Can't rename viminfo file to %s!
-// Now only five of them are used:
+// Now only six of them are used:
// E137: ShaDa file is not writeable (for pre-open checks)
// E138: All %s.tmp.X files exist, cannot write ShaDa file!
// RCERR (E576) for critical read errors.
@@ -130,6 +127,7 @@ KHASH_SET_INIT_STR(strset)
// RERR (E575) for various errors inside read ShaDa file.
// SERR (E886) for various “system†errors (always contains output of
// strerror)
+// WERR (E574) for various ignorable write errors
/// Common prefix for all errors inside ShaDa file
///
@@ -148,6 +146,9 @@ KHASH_SET_INIT_STR(strset)
/// Common prefix for all “rename†errors
#define RNERR "E136: "
+/// Common prefix for all ignorable “write†errors
+#define WERR "E574: "
+
/// Flags for shada_read_file and children
typedef enum {
kShaDaWantInfo = 1, ///< Load non-mark information
@@ -198,6 +199,9 @@ typedef enum {
///< a ShaDa file.
kSDWriteFailed, ///< Writing was not successfull (e.g. because there
///< was no space left on device).
+ kSDWriteIgnError, ///< Writing resulted in a error which can be ignored
+ ///< (e.g. when trying to dump a function reference or
+ ///< self-referencing container in a variable).
} ShaDaWriteResult;
/// Flags for shada_read_next_item
@@ -279,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;
@@ -471,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,
@@ -754,9 +758,9 @@ static void close_sd_writer(ShaDaWriteDef *const sd_writer)
FUNC_ATTR_NONNULL_ALL
{
const int fd = (int)(intptr_t) sd_writer->cookie;
- if (fsync(fd) < 0) {
- emsg2(_(SERR "System error while synchronizing ShaDa file: %s"),
- strerror(errno));
+ if (os_fsync(fd) < 0) {
+ emsgf(_(SERR "System error while synchronizing ShaDa file: %s"),
+ os_strerror(errno));
errno = 0;
}
close_file(fd);
@@ -805,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);
@@ -843,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;
@@ -891,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;
}
@@ -928,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;
}
@@ -975,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);
@@ -1403,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;
}
@@ -1666,25 +1670,30 @@ static char *shada_filename(const char *file)
/// @param[in] entry Entry written.
/// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no
/// restrictions.
-static bool shada_pack_entry(msgpack_packer *const packer,
- ShadaEntry entry,
- const size_t max_kbyte)
+///
+/// @return kSDWriteSuccessfull, kSDWriteFailed or kSDWriteIgnError.
+static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
+ ShadaEntry entry,
+ const size_t max_kbyte)
FUNC_ATTR_NONNULL_ALL
{
+ ShaDaWriteResult ret = kSDWriteFailed;
msgpack_sbuffer sbuf;
msgpack_sbuffer_init(&sbuf);
msgpack_packer *spacker = msgpack_packer_new(&sbuf, &msgpack_sbuffer_write);
-#define DUMP_ADDITIONAL_ELEMENTS(src) \
+#define DUMP_ADDITIONAL_ELEMENTS(src, what) \
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) == FAIL) { \
+ if (encode_vim_to_msgpack(spacker, &li->li_tv, \
+ _("additional elements of ShaDa " what)) \
+ == FAIL) { \
goto shada_pack_entry_error; \
} \
} \
} \
} while (0)
-#define DUMP_ADDITIONAL_DATA(src) \
+#define DUMP_ADDITIONAL_DATA(src, what) \
do { \
dict_T *const d = (src); \
if (d != NULL) { \
@@ -1696,7 +1705,9 @@ static bool 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) == FAIL) { \
+ if (encode_vim_to_msgpack(spacker, &di->di_tv, \
+ _("additional data of ShaDa " what)) \
+ == FAIL) { \
goto shada_pack_entry_error; \
} \
} \
@@ -1731,7 +1742,8 @@ static bool shada_pack_entry(msgpack_packer *const packer,
if (is_hist_search) {
msgpack_pack_uint8(spacker, (uint8_t) entry.data.history_item.sep);
}
- DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements);
+ DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements,
+ "history entry item");
break;
}
case kSDItemVariable: {
@@ -1740,11 +1752,20 @@ static bool shada_pack_entry(msgpack_packer *const packer,
? 0
: entry.data.global_var.additional_elements->lv_len);
msgpack_pack_array(spacker, arr_size);
- PACK_BIN(cstr_as_string(entry.data.global_var.name));
- if (vim_to_msgpack(spacker, &entry.data.global_var.value) == FAIL) {
+ const String varname = cstr_as_string(entry.data.global_var.name);
+ PACK_BIN(varname);
+ char vardesc[256] = "variable g:";
+ memcpy(&vardesc[sizeof("variable g:") - 1], varname.data,
+ varname.size + 1);
+ if (encode_vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc)
+ == FAIL) {
+ ret = kSDWriteIgnError;
+ EMSG2(_(WERR "Failed to write variable %s"),
+ entry.data.global_var.name);
goto shada_pack_entry_error;
}
- DUMP_ADDITIONAL_ELEMENTS(entry.data.global_var.additional_elements);
+ DUMP_ADDITIONAL_ELEMENTS(entry.data.global_var.additional_elements,
+ "variable item");
break;
}
case kSDItemSubString: {
@@ -1754,7 +1775,8 @@ static bool shada_pack_entry(msgpack_packer *const packer,
: entry.data.sub_string.additional_elements->lv_len);
msgpack_pack_array(spacker, arr_size);
PACK_BIN(cstr_as_string(entry.data.sub_string.sub));
- DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements);
+ DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements,
+ "sub string item");
break;
}
case kSDItemSearchPattern: {
@@ -1801,7 +1823,8 @@ static bool shada_pack_entry(msgpack_packer *const packer,
msgpack_pack_int64(spacker, entry.data.search_pattern.offset);
}
#undef PACK_BOOL
- DUMP_ADDITIONAL_DATA(entry.data.search_pattern.additional_data);
+ DUMP_ADDITIONAL_DATA(entry.data.search_pattern.additional_data,
+ "search pattern item");
break;
}
case kSDItemChange:
@@ -1836,7 +1859,8 @@ static bool shada_pack_entry(msgpack_packer *const packer,
PACK_STATIC_STR(KEY_NAME_CHAR);
msgpack_pack_uint8(spacker, (uint8_t) entry.data.filemark.name);
}
- DUMP_ADDITIONAL_DATA(entry.data.filemark.additional_data);
+ DUMP_ADDITIONAL_DATA(entry.data.filemark.additional_data,
+ "mark (change, jump, global or local) item");
break;
}
case kSDItemRegister: {
@@ -1858,13 +1882,13 @@ static bool 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);
msgpack_pack_uint64(spacker, (uint64_t) entry.data.reg.width);
}
- DUMP_ADDITIONAL_DATA(entry.data.reg.additional_data);
+ DUMP_ADDITIONAL_DATA(entry.data.reg.additional_data, "register item");
break;
}
case kSDItemBufferList: {
@@ -1895,7 +1919,8 @@ static bool shada_pack_entry(msgpack_packer *const packer,
msgpack_pack_uint64(
spacker, (uint64_t) entry.data.buffer_list.buffers[i].pos.col);
}
- DUMP_ADDITIONAL_DATA(entry.data.buffer_list.buffers[i].additional_data);
+ DUMP_ADDITIONAL_DATA(entry.data.buffer_list.buffers[i].additional_data,
+ "buffer list subitem");
}
break;
}
@@ -1946,11 +1971,11 @@ static bool shada_pack_entry(msgpack_packer *const packer,
}
msgpack_packer_free(spacker);
msgpack_sbuffer_destroy(&sbuf);
- return true;
+ return kSDWriteSuccessfull;
shada_pack_entry_error:
msgpack_packer_free(spacker);
msgpack_sbuffer_destroy(&sbuf);
- return false;
+ return ret;
}
#undef PACK_STRING
@@ -1965,13 +1990,13 @@ shada_pack_entry_error:
/// is assumed that entry was already converted.
/// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no
/// restrictions.
-static bool shada_pack_encoded_entry(msgpack_packer *const packer,
- const vimconv_T *const sd_conv,
- PossiblyFreedShadaEntry entry,
- const size_t max_kbyte)
+static ShaDaWriteResult shada_pack_encoded_entry(msgpack_packer *const packer,
+ const vimconv_T *const sd_conv,
+ PossiblyFreedShadaEntry entry,
+ const size_t max_kbyte)
FUNC_ATTR_NONNULL_ALL
{
- bool ret = true;
+ ShaDaWriteResult ret = kSDWriteSuccessfull;
if (entry.can_free_entry) {
ret = shada_pack_entry(packer, entry.data, max_kbyte);
shada_free_shada_entry(&entry.data);
@@ -2134,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;
@@ -2151,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;
@@ -2159,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;
@@ -2244,9 +2269,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
assert(false);
}
case kSDItemUnknown: {
- if (!shada_pack_entry(packer, entry, 0)) {
- ret = kSDWriteFailed;
- }
+ ret = shada_pack_entry(packer, entry, 0);
shada_free_shada_entry(&entry);
break;
}
@@ -2262,9 +2285,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
}
case kSDItemHistoryEntry: {
if (entry.data.history_item.histtype >= HIST_COUNT) {
- if (!shada_pack_entry(packer, entry, 0)) {
- ret = kSDWriteFailed;
- }
+ ret = shada_pack_entry(packer, entry, 0);
shada_free_shada_entry(&entry);
break;
}
@@ -2275,9 +2296,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
case kSDItemRegister: {
const int idx = op_reg_index(entry.data.reg.name);
if (idx < 0) {
- if (!shada_pack_entry(packer, entry, 0)) {
- ret = kSDWriteFailed;
- }
+ ret = shada_pack_entry(packer, entry, 0);
shada_free_shada_entry(&entry);
break;
}
@@ -2286,9 +2305,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
}
case kSDItemVariable: {
if (!in_strset(&wms->dumped_variables, entry.data.global_var.name)) {
- if (!shada_pack_entry(packer, entry, 0)) {
- ret = kSDWriteFailed;
- }
+ ret = shada_pack_entry(packer, entry, 0);
}
shada_free_shada_entry(&entry);
break;
@@ -2296,9 +2313,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
case kSDItemGlobalMark: {
const int idx = mark_global_index(entry.data.filemark.name);
if (idx < 0) {
- if (!shada_pack_entry(packer, entry, 0)) {
- ret = kSDWriteFailed;
- }
+ ret = shada_pack_entry(packer, entry, 0);
shada_free_shada_entry(&entry);
break;
}
@@ -2465,7 +2480,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
}
// Write header
- if (!shada_pack_entry(packer, (ShadaEntry) {
+ if (shada_pack_entry(packer, (ShadaEntry) {
.type = kSDItemHeader,
.timestamp = os_time(),
.data = {
@@ -2486,7 +2501,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
}),
}
}
- }, 0)) {
+ }, 0) == kSDWriteFailed) {
ret = kSDWriteFailed;
goto shada_write_exit;
}
@@ -2526,7 +2541,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
};
i++;
}
- if (!shada_pack_entry(packer, buflist_entry, 0)) {
+ if (shada_pack_entry(packer, buflist_entry, 0) == kSDWriteFailed) {
xfree(buflist_entry.data.buffer_list.buffers);
ret = kSDWriteFailed;
goto shada_write_exit;
@@ -2552,7 +2567,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
} else {
copy_tv(&vartv, &tgttv);
}
- if (!shada_pack_entry(packer, (ShadaEntry) {
+ ShaDaWriteResult spe_ret;
+ if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) {
.type = kSDItemVariable,
.timestamp = cur_timestamp,
.data = {
@@ -2562,7 +2578,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
.additional_elements = NULL,
}
}
- }, max_kbyte)) {
+ }, max_kbyte)) == kSDWriteFailed) {
clear_tv(&vartv);
clear_tv(&tgttv);
ret = kSDWriteFailed;
@@ -2570,8 +2586,10 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
}
clear_tv(&vartv);
clear_tv(&tgttv);
- int kh_ret;
- (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret);
+ if (spe_ret == kSDWriteSuccessfull) {
+ int kh_ret;
+ (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret);
+ }
} while (var_iter != NULL);
}
@@ -2739,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,
}
@@ -2828,9 +2846,9 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
do { \
for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \
if (wms_array[i_].data.type != kSDItemMissing) { \
- if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv, \
- wms_array[i_], \
- max_kbyte)) { \
+ if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, \
+ wms_array[i_], \
+ max_kbyte) == kSDWriteFailed) { \
ret = kSDWriteFailed; \
goto shada_write_exit; \
} \
@@ -2840,8 +2858,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
PACK_WMS_ARRAY(wms->global_marks);
PACK_WMS_ARRAY(wms->registers);
for (size_t i = 0; i < wms->jumps_size; i++) {
- if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms->jumps[i],
- max_kbyte)) {
+ if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms->jumps[i],
+ max_kbyte) == kSDWriteFailed) {
ret = kSDWriteFailed;
goto shada_write_exit;
}
@@ -2849,8 +2867,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
#define PACK_WMS_ENTRY(wms_entry) \
do { \
if (wms_entry.data.type != kSDItemMissing) { \
- if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_entry, \
- max_kbyte)) { \
+ if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_entry, \
+ max_kbyte) == kSDWriteFailed) { \
ret = kSDWriteFailed; \
goto shada_write_exit; \
} \
@@ -2877,16 +2895,16 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
for (size_t i = 0; i < file_markss_to_dump; i++) {
PACK_WMS_ARRAY(all_file_markss[i]->marks);
for (size_t j = 0; j < all_file_markss[i]->changes_size; j++) {
- if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv,
- all_file_markss[i]->changes[j],
- max_kbyte)) {
+ if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv,
+ all_file_markss[i]->changes[j],
+ max_kbyte) == kSDWriteFailed) {
ret = kSDWriteFailed;
goto shada_write_exit;
}
}
for (size_t j = 0; j < all_file_markss[i]->additional_marks_size; j++) {
- if (!shada_pack_entry(packer, all_file_markss[i]->additional_marks[j],
- 0)) {
+ if (shada_pack_entry(packer, all_file_markss[i]->additional_marks[j],
+ 0) == kSDWriteFailed) {
shada_free_shada_entry(&all_file_markss[i]->additional_marks[j]);
ret = kSDWriteFailed;
goto shada_write_exit;
@@ -2903,16 +2921,15 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
if (dump_one_history[i]) {
hms_insert_whole_neovim_history(&wms->hms[i]);
HMS_ITER(&wms->hms[i], cur_entry, {
- if (!shada_pack_encoded_entry(
+ if (shada_pack_encoded_entry(
packer, &sd_writer->sd_conv, (PossiblyFreedShadaEntry) {
.data = cur_entry->data,
.can_free_entry = cur_entry->can_free_entry,
- }, max_kbyte)) {
+ }, max_kbyte) == kSDWriteFailed) {
ret = kSDWriteFailed;
break;
}
})
- hms_dealloc(&wms->hms[i]);
if (ret == kSDWriteFailed) {
goto shada_write_exit;
}
@@ -2921,6 +2938,11 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
}
shada_write_exit:
+ for (size_t i = 0; i < HIST_COUNT; i++) {
+ if (dump_one_history[i]) {
+ hms_dealloc(&wms->hms[i]);
+ }
+ }
kh_dealloc(file_marks, &wms->file_marks);
kh_dealloc(bufset, &removable_bufs);
msgpack_packer_free(packer);
@@ -3043,6 +3065,7 @@ shada_write_file_nomerge: {}
const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge
? NULL
: &sd_reader));
+ assert(sw_ret != kSDWriteIgnError);
#ifndef UNIX
sd_writer.close(&sd_writer);
#endif
@@ -3242,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);
@@ -3281,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);
@@ -3316,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;
@@ -3380,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) \
@@ -3454,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); \
@@ -3479,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; \
@@ -3547,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"),
@@ -3617,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;
}
@@ -3655,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,
@@ -3667,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;
@@ -3676,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);
}
@@ -3693,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");
@@ -3709,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;
@@ -3719,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);
@@ -3735,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);
}
@@ -3755,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);
}
@@ -3764,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;
}
@@ -3796,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;
}
@@ -3844,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;
}
@@ -3868,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;
}
@@ -3889,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;
}
@@ -3911,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) {
@@ -3928,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);
@@ -3953,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 420e8e2b70..0acaa9ae2b 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -319,7 +319,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"
@@ -1063,8 +1062,7 @@ static char_u *repl_to = NULL;
//
// Returns the length of the word in bytes, also when it's OK, so that the
// caller can skip over the word.
-size_t
-spell_check (
+size_t spell_check(
win_T *wp, // current window
char_u *ptr,
hlf_T *attrp,
@@ -1082,12 +1080,14 @@ spell_check (
// A word never starts at a space or a control character. Return quickly
// then, skipping over the character.
- if (*ptr <= ' ')
+ if (*ptr <= ' ') {
return 1;
+ }
// Return here when loading language files failed.
- if (GA_EMPTY(&wp->w_s->b_langp))
+ if (GA_EMPTY(&wp->w_s->b_langp)) {
return 1;
+ }
memset(&mi, 0, sizeof(matchinf_T));
@@ -1095,10 +1095,13 @@ spell_check (
// 0X99FF. But always do check spelling to find "3GPP" and "11
// julifeest".
if (*ptr >= '0' && *ptr <= '9') {
- if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
+ if (*ptr == '0' && (ptr[1] == 'b' || ptr[1] == 'B')) {
+ mi.mi_end = (char_u*) skipbin((char*) ptr + 2);
+ } else if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) {
mi.mi_end = skiphex(ptr + 2);
- else
+ } else {
mi.mi_end = skipdigits(ptr);
+ }
nrlen = (size_t)(mi.mi_end - ptr);
}
@@ -1113,12 +1116,14 @@ spell_check (
if (capcol != NULL && *capcol == 0 && wp->w_s->b_cap_prog != NULL) {
// Check word starting with capital letter.
c = PTR2CHAR(ptr);
- if (!SPELL_ISUPPER(c))
+ if (!SPELL_ISUPPER(c)) {
wrongcaplen = (size_t)(mi.mi_fend - ptr);
+ }
}
}
- if (capcol != NULL)
+ if (capcol != NULL) {
*capcol = -1;
+ }
// We always use the characters up to the next non-word character,
// also for bad words.
@@ -1131,8 +1136,9 @@ spell_check (
// case-fold the word with one non-word character, so that we can check
// for the word end.
- if (*mi.mi_fend != NUL)
+ if (*mi.mi_fend != NUL) {
mb_ptr_adv(mi.mi_fend);
+ }
(void)spell_casefold(ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, MAXWLEN + 1);
mi.mi_fwordlen = (int)STRLEN(mi.mi_fword);
@@ -1149,8 +1155,9 @@ spell_check (
// If reloading fails the language is still in the list but everything
// has been cleared.
- if (mi.mi_lp->lp_slang->sl_fidxs == NULL)
+ if (mi.mi_lp->lp_slang->sl_fidxs == NULL) {
continue;
+ }
// Check for a matching word in case-folded words.
find_word(&mi, FIND_FOLDWORD);
@@ -1181,18 +1188,18 @@ spell_check (
// If we found a number skip over it. Allows for "42nd". Do flag
// rare and local words, e.g., "3GPP".
if (nrlen > 0) {
- if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED)
+ if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED) {
return nrlen;
- }
+ }
+ } else if (!spell_iswordp_nmw(ptr, wp)) {
// When we are at a non-word character there is no error, just
// skip over the character (try looking for a word after it).
- else if (!spell_iswordp_nmw(ptr, wp)) {
if (capcol != NULL && wp->w_s->b_cap_prog != NULL) {
regmatch_T regmatch;
// Check for end of sentence.
regmatch.regprog = wp->w_s->b_cap_prog;
- regmatch.rm_ic = FALSE;
+ regmatch.rm_ic = false;
int r = vim_regexec(&regmatch, ptr, 0);
wp->w_s->b_cap_prog = regmatch.regprog;
if (r) {
@@ -1204,12 +1211,12 @@ spell_check (
return (size_t)(*mb_ptr2len)(ptr);
}
return 1;
- } else if (mi.mi_end == ptr)
+ } else if (mi.mi_end == ptr) {
// Always include at least one character. Required for when there
// is a mixup in "midword".
mb_ptr_adv(mi.mi_end);
- else if (mi.mi_result == SP_BAD
- && LANGP_ENTRY(wp->w_s->b_langp, 0)->lp_slang->sl_nobreak) {
+ } else if (mi.mi_result == SP_BAD
+ && LANGP_ENTRY(wp->w_s->b_langp, 0)->lp_slang->sl_nobreak) {
char_u *p, *fp;
int save_result = mi.mi_result;
@@ -1219,11 +1226,12 @@ spell_check (
if (mi.mi_lp->lp_slang->sl_fidxs != NULL) {
p = mi.mi_word;
fp = mi.mi_fword;
- for (;; ) {
+ for (;;) {
mb_ptr_adv(p);
mb_ptr_adv(fp);
- if (p >= mi.mi_end)
+ if (p >= mi.mi_end) {
break;
+ }
mi.mi_compoff = (int)(fp - mi.mi_fword);
find_word(&mi, FIND_COMPOUND);
if (mi.mi_result != SP_BAD) {
@@ -1235,12 +1243,13 @@ spell_check (
}
}
- if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED)
+ if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED) {
*attrp = HLF_SPB;
- else if (mi.mi_result == SP_RARE)
+ } else if (mi.mi_result == SP_RARE) {
*attrp = HLF_SPR;
- else
+ } else {
*attrp = HLF_SPL;
+ }
}
if (wrongcaplen > 0 && (mi.mi_result == SP_OK || mi.mi_result == SP_RARE)) {
@@ -2322,14 +2331,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.
@@ -12900,8 +12912,8 @@ void ex_spelldump(exarg_T *eap)
do_cmdline_cmd("new");
// enable spelling locally in the new window
- set_option_value((char_u*)"spell", TRUE, (char_u*)"", OPT_LOCAL);
- set_option_value((char_u*)"spl", dummy, spl, OPT_LOCAL);
+ set_option_value((char_u*)"spell", true, (char_u*)"", OPT_LOCAL);
+ set_option_value((char_u*)"spl", dummy, spl, OPT_LOCAL);
xfree(spl);
if (!bufempty() || !buf_valid(curbuf))
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 00dcf3cf46..37a0fb82da 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -119,6 +119,54 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars,
return escaped_string;
}
+/// Save a copy of an unquoted string
+///
+/// Turns string like `a\bc"def\"ghi\\\n"jkl` into `a\bcdef"ghi\\njkl`, for use
+/// in shell_build_argv: the only purpose of backslash is making next character
+/// be treated literally inside the double quotes, if this character is
+/// backslash or quote.
+///
+/// @param[in] string String to copy.
+/// @param[in] length Length of the string to copy.
+///
+/// @return [allocated] Copy of the string.
+char *vim_strnsave_unquoted(const char *const string, const size_t length)
+ FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_RET
+{
+#define ESCAPE_COND(p, inquote, string_end) \
+ (*p == '\\' && inquote && p + 1 < string_end && (p[1] == '\\' || p[1] == '"'))
+ size_t ret_length = 0;
+ bool inquote = false;
+ const char *const string_end = string + length;
+ for (const char *p = string; p < string_end; p++) {
+ if (*p == '"') {
+ inquote = !inquote;
+ } else if (ESCAPE_COND(p, inquote, string_end)) {
+ ret_length++;
+ p++;
+ } else {
+ ret_length++;
+ }
+ }
+
+ char *const ret = xmallocz(ret_length);
+ char *rp = ret;
+ inquote = false;
+ for (const char *p = string; p < string_end; p++) {
+ if (*p == '"') {
+ inquote = !inquote;
+ } else if (ESCAPE_COND(p, inquote, string_end)) {
+ *rp++ = *(++p);
+ } else {
+ *rp++ = *p;
+ }
+ }
+#undef ESCAPE_COND
+
+ return ret;
+}
+
/*
* Escape "string" for use as a shell argument with system().
* This uses single quotes, except when we know we need to use double quotes
@@ -377,9 +425,13 @@ char_u *vim_strchr(const char_u *string, int c)
const char_u *p = string;
if (enc_utf8 && c >= 0x80) {
while (*p != NUL) {
- if (utf_ptr2char(p) == c)
+ int l = (*mb_ptr2len)(p);
+
+ // Avoid matching an illegal byte here.
+ if (l > 1 && utf_ptr2char(p) == c) {
return (char_u *) p;
- p += (*mb_ptr2len)(p);
+ }
+ p += l;
}
return NULL;
}
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 24422c71fb..b04180ad1c 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.
@@ -2613,33 +2615,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 +3010,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 +3292,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 +4205,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 +4229,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 +4482,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 +4868,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 +5008,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 +5425,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 +5539,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);
@@ -6365,7 +6401,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)
@@ -6903,7 +6939,12 @@ 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);
+
+ 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.cterm_ae_attr != 0 || at_en.rgb_ae_attr != 0) {
+ sgp->sg_attr = get_attr_entry(&at_en);
+ }
}
/*
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index d832924efd..7885d467d8 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -445,17 +445,10 @@ do_tag (
tagmatchname = vim_strsave(name);
}
- /*
- * If a count is supplied to the ":tag <name>" command, then
- * jump to count'th matching tag.
- */
- if (type == DT_TAG && *tag != NUL && count > 0)
- cur_match = count - 1;
-
- if (type == DT_SELECT || type == DT_JUMP
- || type == DT_LTAG
- )
+ if (type == DT_TAG || type == DT_SELECT || type == DT_JUMP
+ || type == DT_LTAG) {
cur_match = MAXCOL - 1;
+ }
max_num_matches = cur_match + 1;
/* when the argument starts with '/', use it as a regexp */
@@ -506,18 +499,19 @@ do_tag (
EMSG2(_("E426: tag not found: %s"), name);
g_do_tagpreview = 0;
} else {
- int ask_for_selection = FALSE;
+ bool ask_for_selection = false;
if (type == DT_CSCOPE && num_matches > 1) {
cs_print_tags();
- ask_for_selection = TRUE;
- } else if (type == DT_SELECT ||
- (type == DT_JUMP && num_matches > 1)) {
- /*
- * List all the matching tags.
- * Assume that the first match indicates how long the tags can
- * be, and align the file names to that.
- */
+ ask_for_selection = true;
+ } else if (type == DT_TAG) {
+ // If a count is supplied to the ":tag <name>" command, then
+ // jump to count'th matching tag.
+ cur_match = count > 0 ? count - 1 : 0;
+ } else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) {
+ // List all the matching tags.
+ // Assume that the first match indicates how long the tags can
+ // be, and align the file names to that.
parse_match(matches[0], &tagp);
taglen = (int)(tagp.tagname_end - tagp.tagname + 2);
if (taglen < 18)
@@ -532,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,
@@ -666,9 +658,10 @@ do_tag (
msg_putchar('\n');
os_breakcheck();
}
- if (got_int)
- got_int = FALSE; /* only stop the listing */
- ask_for_selection = TRUE;
+ if (got_int) {
+ got_int = false; // only stop the listing
+ }
+ ask_for_selection = true;
} else if (type == DT_LTAG) {
list_T *list;
char_u tag_name[128 + 1];
@@ -801,10 +794,8 @@ do_tag (
cur_match = 0; /* Jump to the first tag */
}
- if (ask_for_selection == TRUE) {
- /*
- * Ask to select a tag from the list.
- */
+ if (ask_for_selection) {
+ // Ask to select a tag from the list.
i = prompt_for_number(NULL);
if (i <= 0 || i > num_matches || got_int) {
/* no valid choice: don't change anything */
@@ -851,7 +842,7 @@ do_tag (
ic = (matches[cur_match][0] & MT_IC_OFF);
- if (type != DT_SELECT && type != DT_JUMP
+ if (type != DT_TAG && type != DT_SELECT && type != DT_JUMP
&& type != DT_CSCOPE
&& (num_matches > 1 || ic)
&& !skip_msg) {
@@ -879,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.
@@ -1154,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;
@@ -1217,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[]. */
@@ -1962,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 adf3f725a2..104cc47cda 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -1,5 +1,5 @@
-// VT220/xterm-like terminal emulator implementation for Neovim. Powered by
-// libvterm(http://www.leonerd.org.uk/code/libvterm/).
+// VT220/xterm-like terminal emulator implementation for nvim. Powered by
+// libvterm (http://www.leonerd.org.uk/code/libvterm/).
//
// libvterm is a pure C99 terminal emulation library with abstract input and
// display. This means that the library needs to read data from the master fd
@@ -10,31 +10,31 @@
// vterm_keyboard_key/vterm_keyboard_unichar, which generates byte streams that
// must be fed back to the master fd.
//
-// This implementation uses Neovim buffers as the display mechanism for both
+// This implementation uses nvim buffers as the display mechanism for both
// the visible screen and the scrollback buffer. When focused, the window
// "pins" to the bottom of the buffer and mirrors libvterm screen state.
//
// When a line becomes invisible due to a decrease in screen height or because
// a line was pushed up during normal terminal output, we store the line
-// information in the scrollback buffer, which is mirrored in the Neovim buffer
+// information in the scrollback buffer, which is mirrored in the nvim buffer
// by appending lines just above the visible part of the buffer.
//
// When the screen height increases, libvterm will ask for a row in the
-// scrollback buffer, which is mirrored in the Neovim buffer displaying lines
+// scrollback buffer, which is mirrored in the nvim buffer displaying lines
// that were previously invisible.
//
-// The vterm->Neovim synchronization is performed in intervals of 10
+// The vterm->nvim synchronization is performed in intervals of 10
// milliseconds. This is done to minimize screen updates when receiving large
// bursts of data.
//
// This module is decoupled from the processes that normally feed it data, so
-// it's possible to use it as a general purpose console buffer(possibly as a
-// log/display mechanism for Neovim in the future)
+// it's possible to use it as a general purpose console buffer (possibly as a
+// log/display mechanism for nvim in the future)
//
-// Inspired by vimshell(http://www.wana.at/vimshell/) and
-// Conque(https://code.google.com/p/conque/). Libvterm usage instructions (plus
+// Inspired by vimshell (http://www.wana.at/vimshell/) and
+// Conque (https://code.google.com/p/conque/). Libvterm usage instructions (plus
// some extra code) were taken from
-// pangoterm(http://www.leonerd.org.uk/code/pangoterm/)
+// pangoterm (http://www.leonerd.org.uk/code/pangoterm/)
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
@@ -77,9 +77,7 @@
typedef struct terminal_state {
VimState state;
Terminal *term;
- int save_state; // saved value of State
int save_rd; // saved value of RedrawingDisabled
- bool save_mapped_ctrl_c; // saved value of mapped_ctrl_c;
bool close;
bool got_bs; // if the last input was <C-\>
} TerminalState;
@@ -143,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;
};
@@ -181,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);
}
@@ -207,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;
@@ -222,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);
@@ -238,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:
//
@@ -252,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);
@@ -267,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;
@@ -290,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.
@@ -306,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)
@@ -362,12 +383,11 @@ void terminal_enter(void)
checkpcmark();
setpcmark();
- s->save_state = State;
+ int save_state = State;
s->save_rd = RedrawingDisabled;
State = TERM_FOCUS;
+ mapped_ctrl_c |= TERM_FOCUS; // Always map CTRL-C to avoid interrupt.
RedrawingDisabled = false;
- s->save_mapped_ctrl_c = mapped_ctrl_c;
- mapped_ctrl_c = true;
// go to the bottom when the terminal is focused
adjust_topline(s->term, buf, false);
// erase the unfocused cursor
@@ -380,11 +400,10 @@ void terminal_enter(void)
state_enter(&s->state);
restart_edit = 0;
- State = s->save_state;
+ State = save_state;
RedrawingDisabled = s->save_rd;
// draw the unfocused cursor
invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1);
- mapped_ctrl_c = s->save_mapped_ctrl_c;
unshowmode(true);
redraw(curbuf->handle != s->term->buf_handle);
ui_busy_stop();
@@ -402,14 +421,18 @@ static int terminal_execute(VimState *state, int key)
TerminalState *s = (TerminalState *)state;
switch (key) {
- case K_FOCUSGAINED: // Neovim has been given focus
+ case K_FOCUSGAINED: // nvim has been given focus
apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
break;
- case K_FOCUSLOST: // Neovim has lost focus
+ case K_FOCUSLOST: // nvim has lost focus
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:
@@ -543,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)
@@ -565,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,
});
}
@@ -626,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 8c3e99c624..b763a67347 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -7,28 +7,41 @@ export SHELL := sh
VIMPROG := ../../../build/bin/nvim
SCRIPTSOURCE := ../../../runtime
-SCRIPTS := test_eval.out \
- 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 test39.out test40.out \
- test42.out test45.out \
- test47.out test48.out test49.out \
- test52.out test53.out test55.out \
- test64.out \
- test68.out test69.out \
- test73.out \
- test79.out \
- test83.out \
- test88.out \
- test_listlbr.out \
- test_breakindent.out \
- test_charsearch.out \
- test_close_count.out \
- test_command_count.out \
+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_listlbr.out \
+ test_breakindent.out \
+ test_close_count.out \
+ test_marks.out \
+
+# Tests using runtest.vim.vim.
+# Keep test_alot*.res as the last one, sort the others.
+NEW_TESTS = \
+ test_viml.res \
+ test_cursor_func.res \
+ test_alot.res
SCRIPTS_GUI := test16.out
@@ -61,9 +74,9 @@ ifdef TESTNUM
SCRIPTS := test$(TESTNUM).out
endif
-nongui: nolog $(SCRIPTS) report
+nongui: nolog $(SCRIPTS) newtests report
-gui: nolog $(SCRIPTS) $(SCRIPTS_GUI) report
+gui: nolog $(SCRIPTS) $(SCRIPTS_GUI) newtests report
.gdbinit:
echo 'set $$_exitcode = -1\nrun\nif $$_exitcode != -1\n quit\nend' > .gdbinit
@@ -90,9 +103,11 @@ RUN_VIM := VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(TOOL) $(VIMPROG)
clean:
-rm -rf *.out \
*.failed \
+ *.res \
*.rej \
*.orig \
test.log \
+ messages \
$(RM_ON_RUN) \
$(RM_ON_START) \
valgrind.* \
@@ -128,7 +143,7 @@ test1.out: .gdbinit test1.in
# Check if the test.out file matches test.ok.
@/bin/sh -c "if test -f test.out; then \
- if diff test.out $*.ok; then \
+ if diff -u test.out $*.ok; then \
mv -f test.out $*.out; \
else \
echo $* FAILED >> test.log; \
@@ -145,4 +160,20 @@ test1.out: .gdbinit test1.in
test49.out: test49.vim
nolog:
- -rm -f test.log
+ -rm -f test.log messages
+
+
+# New style of tests uses Vim script with assert calls. These are easier
+# to write and a lot easier to read and debug.
+# Limitation: Only works with the +eval feature.
+RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(VIMPROG) -u unix.vim -U NONE --noplugin
+
+newtests: newtestssilent
+ @/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then \
+ cat messages && cat test.log; \
+ fi"
+
+newtestssilent: $(NEW_TESTS)
+
+%.res: %.vim .gdbinit
+ $(RUN_VIMTEST) -u NONE -S runtest.vim $*.vim
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
new file mode 100644
index 0000000000..6601dcf52f
--- /dev/null
+++ b/src/nvim/testdir/runtest.vim
@@ -0,0 +1,128 @@
+" This script is sourced while editing the .vim file with the tests.
+" When the script is successful the .res file will be created.
+" Errors are appended to the test.log file.
+"
+" The test script may contain anything, only functions that start with
+" "Test_" are special. These will be invoked and should contain assert
+" functions. See test_assert.vim for an example.
+"
+" It is possible to source other files that contain "Test_" functions. This
+" can speed up testing, since Vim does not need to restart. But be careful
+" that the tests do not interfere with each other.
+"
+" If an error cannot be detected properly with an assert function add the
+" error to the v:errors list:
+" call add(v:errors, 'test foo failed: Cannot find xyz')
+"
+" If preparation for each Test_ function is needed, define a SetUp function.
+" It will be called before each Test_ function.
+"
+" 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'
+ echoerr error
+ split test.log
+ $put =error
+ w
+ 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('%')
+let done = 0
+let fail = 0
+let errors = []
+let messages = []
+if expand('%') =~ 'test_viml.vim'
+ " this test has intentional errors, don't use try/catch.
+ source %
+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
+ if exists("*SetUp")
+ call SetUp()
+ endif
+
+ call add(messages, 'Executing ' . test)
+ let done += 1
+ try
+ exe 'call ' . test
+ catch
+ let fail += 1
+ call add(v:errors, 'Caught exception in ' . test . ': ' . v:exception . ' @ ' . v:throwpoint)
+ endtry
+
+ if len(v:errors) > 0
+ let fail += 1
+ call add(errors, 'Found errors in ' . test . ':')
+ call extend(errors, v:errors)
+ let v:errors = []
+ endif
+
+ if exists("*TearDown")
+ call TearDown()
+ endif
+endfor
+
+if fail == 0
+ " Success, create the .res file so that make knows it's done.
+ exe 'split ' . fnamemodify(testname, ':r') . '.res'
+ write
+endif
+
+if len(errors) > 0
+ " Append errors to test.log
+ split test.log
+ call append(line('$'), '')
+ call append(line('$'), 'From ' . testname . ':')
+ call append(line('$'), errors)
+ write
+endif
+
+let message = 'Executed ' . done . (done > 1 ? ' tests': ' test')
+echo message
+call add(messages, message)
+if fail > 0
+ let message = fail . ' FAILED'
+ echo message
+ call add(messages, message)
+endif
+
+" Append messages to "messages"
+split messages
+call append(line('$'), '')
+call append(line('$'), 'From ' . testname . ':')
+call append(line('$'), messages)
+write
+
+qall!
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/test13.in b/src/nvim/testdir/test13.in
index cb8a6fff89..6713f80e88 100644
--- a/src/nvim/testdir/test13.in
+++ b/src/nvim/testdir/test13.in
@@ -48,6 +48,12 @@ otestje3
:au BufWipeout Xtestje1 buf Xtestje1
:bwipe
:w >>test.out
+:only
+:new|set buftype=help
+:wincmd w
+:1quit
+:$put ='Final line'
+:$w >>test.out
:qa!
ENDTEST
diff --git a/src/nvim/testdir/test13.ok b/src/nvim/testdir/test13.ok
index 0f1fc347a4..66ebce63f7 100644
--- a/src/nvim/testdir/test13.ok
+++ b/src/nvim/testdir/test13.ok
@@ -28,3 +28,4 @@ testje1
contents
contents
end of testfile
+Final line
diff --git a/src/nvim/testdir/test17.in b/src/nvim/testdir/test17.in
index 7fef87d383..a8c81b832d 100644
--- a/src/nvim/testdir/test17.in
+++ b/src/nvim/testdir/test17.in
@@ -41,17 +41,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 +71,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 +104,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 3f7b9eb472..2a89eac73d 100644
--- a/src/nvim/testdir/test30.in
+++ b/src/nvim/testdir/test30.in
@@ -7,32 +7,27 @@ STARTTEST
:" first write three test files, one in each format
:set fileformat=unix
:set fileformats=
-:/^1/w! XX1
-:/^2/w! XX2
-:/^3/w! XX3
-:/^4/w! XX4
-:/^5/w! XX5
-:/^6/w! XX6
-:/^7/w! XX7
-:/^8/w! XX8
-:/^9/w! XX9
-:/^10/w! XX10
:/^unix/;/eof/-1w! XXUnix
:/^dos/;/eof/-1w! XXDos
:set bin noeol
:$w! XXMac
+Gonoeol
+:$w! XXEol
:set nobin eol
+:enew!
:bwipe XXUnix XXDos XXMac
:" create mixed format files
:if has("win32")
: !copy /b XXUnix+XXDos XXUxDs
: !copy /b XXUnix+XXMac XXUxMac
: !copy /b XXDos+XXMac XXDosMac
+: !copy /b XXMac+XXEol XXMacEol
: !copy /b XXUnix+XXDos+XXMac XXUxDsMc
:else
: !cat XXUnix XXDos >XXUxDs
: !cat XXUnix XXMac >XXUxMac
: !cat XXDos XXMac >XXDosMac
+: !cat XXMac XXEol >XXMacEol
: !cat XXUnix XXDos XXMac >XXUxDsMc
:endif
:"
@@ -97,26 +92,48 @@ STARTTEST
:e! XXDosMac
:w! XXtt53
:bwipe XXDosMac
+:e! XXEol
+ggO=&ffs
+:=&ff
+:w! XXtt54
+:bwipe XXEol
:set fileformats=dos,mac
:e! XXUxDs
:w! XXtt61
:bwipe XXUxDs
:e! XXUxMac
-:w! XXtt62
+ggO=&ffs
+:=&ff
+:w! XXtt62
:bwipe XXUxMac
:e! XXUxDsMc
:w! XXtt63
:bwipe XXUxDsMc
+:e! XXMacEol
+ggO=&ffs
+:=&ff
+:w! XXtt64
+:bwipe XXMacEol
:"
:" try reading and writing with 'fileformats' set to three formats
:set fileformats=unix,dos,mac
:e! XXUxDsMc
:w! XXtt71
:bwipe XXUxDsMc
+:e! XXEol
+ggO=&ffs
+:=&ff
+:w! XXtt72
+:bwipe XXEol
:set fileformats=mac,dos,unix
:e! XXUxDsMc
:w! XXtt81
:bwipe XXUxDsMc
+:e! XXEol
+ggO=&ffs
+:=&ff
+:w! XXtt82
+:bwipe XXEol
:" try with 'binary' set
:set fileformats=mac,unix,dos
:set binary
@@ -150,11 +167,15 @@ ggdGaEND:w >>XXtt01
:w >>XXtt51
:w >>XXtt52
:w >>XXtt53
+:w >>XXtt54
:w >>XXtt61
:w >>XXtt62
:w >>XXtt63
+:w >>XXtt64
:w >>XXtt71
+:w >>XXtt72
:w >>XXtt81
+:w >>XXtt82
:w >>XXtt91
:w >>XXtt92
:w >>XXtt93
@@ -181,11 +202,15 @@ Go4:$r XXtt41
Go5:$r XXtt51
:$r XXtt52
:$r XXtt53
+:$r XXtt54
Go6:$r XXtt61
:$r XXtt62
:$r XXtt63
+:$r XXtt64
Go7:$r XXtt71
+:$r XXtt72
Go8:$r XXtt81
+:$r XXtt82
Go9:$r XXtt91
:$r XXtt92
:$r XXtt93
@@ -195,17 +220,6 @@ Go10:$r XXUnix
:qa!
ENDTEST
-1
-2
-3
-4
-5
-6
-7
-8
-9
-10
-
unix
unix
eof
diff --git a/src/nvim/testdir/test30.ok b/src/nvim/testdir/test30.ok
index 380ce67061..b35f4f5904 100644
--- a/src/nvim/testdir/test30.ok
+++ b/src/nvim/testdir/test30.ok
@@ -70,12 +70,16 @@ END
dos
dos
mac mac END
+unix,mac:unix
+noeol
+END
6
unix
unix
dos
dos
END
+dos,mac:dos
unix
unix
mac mac
@@ -86,6 +90,7 @@ dos
dos
mac mac
END
+dos,mac:mac mac mac noeol END
7
unix
unix
@@ -93,6 +98,9 @@ dos
dos
mac mac
END
+unix,dos,mac:unix
+noeol
+END
8
unix
unix
@@ -100,6 +108,7 @@ dos
dos
mac mac
END
+mac,dos,unix:mac noeol END
9
unix
unix
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/test39.in b/src/nvim/testdir/test39.in
deleted file mode 100644
index ebbcbd6d0d..0000000000
--- a/src/nvim/testdir/test39.in
+++ /dev/null
@@ -1,106 +0,0 @@
-
-Test Visual block mode commands
-And test "U" in Visual mode, also on German sharp S.
-
-STARTTEST
-:so small.vim
-:so mbyte.vim
-/^abcde
-:" Test shift-right of a block
-jlllljj>wlljlll>
-:" Test shift-left of a block
-G$hhhhkk<
-:" Test block-insert
-GklkkkIxyz
-:" Test block-replace
-Gllllkkklllrq
-:" Test block-change
-G$khhhhhkkcmno
-:$-4,$w! test.out
-:" Test block-insert using cursor keys for movement
-/^aaaa/
-:exe ":norm! l\<C-V>jjjlllI\<Right>\<Right> \<Esc>"
-:/^aa/,/^$/w >> test.out
-/xaaa$/
-:exe ":norm! \<C-V>jjjI<>\<Left>p\<Esc>"
-:/xaaa$/,/^$/w >> test.out
-:" Test for Visual block was created with the last <C-v>$
-/^A23$/
-:exe ":norm! l\<C-V>j$Aab\<Esc>"
-:.,/^$/w >> test.out
-:" Test for Visual block was created with the middle <C-v>$ (1)
-/^B23$/
-:exe ":norm! l\<C-V>j$hAab\<Esc>"
-:.,/^$/w >> test.out
-:" Test for Visual block was created with the middle <C-v>$ (2)
-/^C23$/
-:exe ":norm! l\<C-V>j$hhAab\<Esc>"
-:.,/^$/w >> test.out
-:" Test for Visual block insert when virtualedit=all and utf-8 encoding
-:set ve=all
-:/\t\tline
-:exe ":norm! 07l\<C-V>jjIx\<Esc>"
-:.,/^$/w >> test.out
-:" Test for Visual block append when virtualedit=all
-:exe ":norm! 012l\<C-v>jjAx\<Esc>"
-:set ve=
-:.,/^$/w >> test.out
-:" gUe must uppercase a whole word, also when ß changes to SS
-Gothe youtußeuu endYpk0wgUe
-:" gUfx must uppercase until x, inclusive.
-O- youßtußexu -0fogUfx
-:" VU must uppercase a whole line
-YpkVU
-:" same, when it's the last line in the buffer
-YPGi111VUddP
-:" Uppercase two lines
-Oblah di
-doh dutVkUj
-:" Uppercase part of two lines
-ddppi333k0i222fyllvjfuUk
-:" visual replace using Enter or NL
-G3o1234567892k05l2jr G3o987652k02l2jr
-G3o1234567892k05l2jr
-G3o987652k02l2jr
-:"
-:" Test cursor position. When ve=block and Visual block mode and $gj
-:set ve=block
-:exe ":norm! 2k\<C-V>$gj\<Esc>"
-:let cpos=getpos("'>")
-:$put ='col:'.cpos[2].' off:'.cpos[3]
-:/^the/,$w >> test.out
-:qa!
-ENDTEST
-
- line1
- line2
- line3
-
-aaaaaa
-bbbbbb
-cccccc
-dddddd
-
-xaaa
-bbbb
-cccc
-dddd
-
-yaaa
-¿¿¿
-bbb
-
-A23
-4567
-
-B23
-4567
-
-C23
-4567
-
-abcdefghijklm
-abcdefghijklm
-abcdefghijklm
-abcdefghijklm
-abcdefghijklm
diff --git a/src/nvim/testdir/test39.ok b/src/nvim/testdir/test39.ok
deleted file mode 100644
index 198e5b14dc..0000000000
--- a/src/nvim/testdir/test39.ok
+++ /dev/null
Binary files differ
diff --git a/src/nvim/testdir/test45.in b/src/nvim/testdir/test45.in
deleted file mode 100644
index e5af5073d9..0000000000
--- a/src/nvim/testdir/test45.in
+++ /dev/null
@@ -1,80 +0,0 @@
-Tests for folding. vim: set ft=vim :
-
-STARTTEST
-:so small.vim
-:" We also need the +syntax feature here.
-:if !has("syntax")
- e! test.ok
- w! test.out
- qa!
-:endif
-:" basic test if a fold can be created, opened, moving to the end and closed
-/^1
-zf2j:call append("$", "manual " . getline(foldclosed(".")))
-zo:call append("$", foldclosed("."))
-]z:call append("$", getline("."))
-zc:call append("$", getline(foldclosed(".")))
-:" test folding with markers.
-:set fdm=marker fdl=1 fdc=3
-/^5
-:call append("$", "marker " . foldlevel("."))
-[z:call append("$", foldlevel("."))
-jo{{ r{jj:call append("$", foldlevel("."))
-kYpj:call append("$", foldlevel("."))
-:" test folding with indent
-:set fdm=indent sw=2
-/^2 b
-i jI :call append("$", "indent " . foldlevel("."))
-k:call append("$", foldlevel("."))
-:" test syntax folding
-:set fdm=syntax fdl=0
-:syn region Hup start="dd" end="ii" fold contains=Fd1,Fd2,Fd3
-:syn region Fd1 start="ee" end="ff" fold contained
-:syn region Fd2 start="gg" end="hh" fold contained
-:syn region Fd3 start="commentstart" end="commentend" fold contained
-Gzk:call append("$", "folding " . getline("."))
-k:call append("$", getline("."))
-jAcommentstart Acommentend:set fdl=1
-3j:call append("$", getline("."))
-:set fdl=0
-zO j:call append("$", getline("."))
-:" test expression folding
-:fun Flvl()
- let l = getline(v:lnum)
- if l =~ "bb$"
- return 2
- elseif l =~ "gg$"
- return "s1"
- elseif l =~ "ii$"
- return ">2"
- elseif l =~ "kk$"
- return "0"
- endif
- return "="
-endfun
-:set fdm=expr fde=Flvl()
-/bb$
-:call append("$", "expr " . foldlevel("."))
-/hh$
-:call append("$", foldlevel("."))
-/ii$
-:call append("$", foldlevel("."))
-/kk$
-:call append("$", foldlevel("."))
-:/^last/+1,$w! test.out
-:delfun Flvl
-:qa!
-ENDTEST
-
-1 aa
-2 bb
-3 cc
-4 dd {{{
-5 ee {{{ }}}
-6 ff }}}
-7 gg
-8 hh
-9 ii
-a jj
-b kk
-last
diff --git a/src/nvim/testdir/test45.ok b/src/nvim/testdir/test45.ok
deleted file mode 100644
index f04996e337..0000000000
--- a/src/nvim/testdir/test45.ok
+++ /dev/null
@@ -1,18 +0,0 @@
-manual 1 aa
--1
-3 cc
-1 aa
-marker 2
-1
-1
-0
-indent 2
-1
-folding 9 ii
- 3 cc
-7 gg
-8 hh
-expr 2
-1
-2
-0
diff --git a/src/nvim/testdir/test47.in b/src/nvim/testdir/test47.in
index 13ad82462f..f15eaf0f8f 100644
--- a/src/nvim/testdir/test47.in
+++ b/src/nvim/testdir/test47.in
@@ -1,5 +1,7 @@
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.
@@ -10,8 +12,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 +49,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 +92,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/test49.in b/src/nvim/testdir/test49.in
index 1ce57246ee..d95052e14c 100644
--- a/src/nvim/testdir/test49.in
+++ b/src/nvim/testdir/test49.in
@@ -8,7 +8,9 @@ STARTTEST
: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/test53.in b/src/nvim/testdir/test53.in
index 8ca9c9ed29..7c35b2e853 100644
--- a/src/nvim/testdir/test53.in
+++ b/src/nvim/testdir/test53.in
@@ -23,6 +23,7 @@ jfXdit
0fXdit
fXdat
0fXdat
+dit
:"
:put =matchstr(\"abcd\", \".\", 0, 2) " b
:put =matchstr(\"abcd\", \"..\", 0, 2) " bc
@@ -97,6 +98,9 @@ voo "nah" sdf " asdf" sdf " sdf" sd
-<b>asdX<i>a<i />sdf</i>asdf</b>-
-<b>asdf<i>Xasdf</i>asdf</b>-
-<b>asdX<i>as<b />df</i>asdf</b>-
+-<b>
+innertext object
+</b>
</begin>
SEARCH:
foobar
diff --git a/src/nvim/testdir/test53.ok b/src/nvim/testdir/test53.ok
index 0c0b9ded16..05206972a4 100644
--- a/src/nvim/testdir/test53.ok
+++ b/src/nvim/testdir/test53.ok
@@ -11,6 +11,7 @@ voo "zzzzzzzzzzzzzzzzzzzzzzzzzzzzsd
-<b></b>-
-<b>asdfasdf</b>-
--
+-<b></b>
</begin>
b
bc
diff --git a/src/nvim/testdir/test55.in b/src/nvim/testdir/test55.in
index c4e82d429c..9e3c1168cc 100644
--- a/src/nvim/testdir/test55.in
+++ b/src/nvim/testdir/test55.in
@@ -282,6 +282,177 @@ let l = [0, 1, 2, 3]
: $put =ps
: endfor
:endfor
+:"
+:" Unletting locked variables
+:$put ='Unletting:'
+:for depth in range(5)
+: $put ='depth is ' . depth
+: for u in range(3)
+: unlet l
+: let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}]
+: exe "lockvar " . depth . " l"
+: if u == 1
+: exe "unlockvar l"
+: elseif u == 2
+: exe "unlockvar " . depth . " l"
+: endif
+: let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]")
+: $put =ps
+: let ps = ''
+: try
+: unlet l[2]['6'][7]
+: let ps .= 'p'
+: catch
+: let ps .= 'F'
+: endtry
+: try
+: unlet l[2][6]
+: let ps .= 'p'
+: catch
+: let ps .= 'F'
+: endtry
+: try
+: unlet l[2]
+: let ps .= 'p'
+: catch
+: let ps .= 'F'
+: endtry
+: try
+: unlet l[1][1][0]
+: let ps .= 'p'
+: catch
+: let ps .= 'F'
+: endtry
+: try
+: unlet l[1][1]
+: let ps .= 'p'
+: catch
+: let ps .= 'F'
+: endtry
+: try
+: unlet l[1]
+: let ps .= 'p'
+: catch
+: let ps .= 'F'
+: endtry
+: try
+: unlet l
+: let ps .= 'p'
+: catch
+: let ps .= 'F'
+: endtry
+: $put =ps
+: endfor
+:endfor
+:"
+:" Locked variables and :unlet or list / dict functions
+:$put ='Locks and commands or functions:'
+:"
+:$put ='No :unlet after lock on dict:'
+:unlet! d
+:let d = {'a': 99, 'b': 100}
+:lockvar 1 d
+:try
+: unlet d.a
+: $put ='did :unlet'
+:catch
+: $put =v:exception[:16]
+:endtry
+:$put =string(d)
+:"
+:$put =':unlet after lock on dict item:'
+:unlet! d
+:let d = {'a': 99, 'b': 100}
+:lockvar d.a
+:try
+: unlet d.a
+: $put ='did :unlet'
+:catch
+: $put =v:exception[:16]
+:endtry
+:$put =string(d)
+:"
+:$put ='filter() after lock on dict item:'
+:unlet! d
+:let d = {'a': 99, 'b': 100}
+:lockvar d.a
+:try
+: call filter(d, 'v:key != "a"')
+: $put ='did filter()'
+:catch
+: $put =v:exception[:16]
+:endtry
+:$put =string(d)
+:"
+:$put ='map() after lock on dict:'
+:unlet! d
+:let d = {'a': 99, 'b': 100}
+:lockvar 1 d
+:try
+: call map(d, 'v:val + 200')
+: $put ='did map()'
+:catch
+: $put =v:exception[:16]
+:endtry
+:$put =string(d)
+:"
+:$put ='No extend() after lock on dict item:'
+:unlet! d
+:let d = {'a': 99, 'b': 100}
+:lockvar d.a
+:try
+: $put =string(extend(d, {'a': 123}))
+: $put ='did extend()'
+:catch
+: $put =v:exception[:14]
+:endtry
+:$put =string(d)
+:"
+:$put ='No remove() of write-protected scope-level variable:'
+:fun! Tfunc(this_is_a_loooooooooong_parameter_name)
+: try
+: $put =string(remove(a:, 'this_is_a_loooooooooong_parameter_name'))
+: $put ='did remove()'
+: catch
+: $put =v:exception[:14]
+: endtry
+:endfun
+:call Tfunc('testval')
+:"
+:$put ='No extend() of write-protected scope-level variable:'
+:fun! Tfunc(this_is_a_loooooooooong_parameter_name)
+: try
+: $put =string(extend(a:, {'this_is_a_loooooooooong_parameter_name': 1234}))
+: $put ='did extend()'
+: catch
+: $put =v:exception[:14]
+: endtry
+:endfun
+:call Tfunc('testval')
+:"
+:$put ='No :unlet of variable in locked scope:'
+:let b:testvar = 123
+:lockvar 1 b:
+:try
+: unlet b:testvar
+: $put ='b:testvar was :unlet: '. (!exists('b:testvar'))
+:catch
+: $put =v:exception[:16]
+:endtry
+: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 ba029b2898..607a95ead9 100644
--- a/src/nvim/testdir/test55.ok
+++ b/src/nvim/testdir/test55.ok
@@ -86,6 +86,67 @@ FFFFFFF
FFpFFpp
0000-000
ppppppp
+Unletting:
+depth is 0
+0000-000
+ppppppp
+0000-000
+ppppppp
+0000-000
+ppppppp
+depth is 1
+1000-000
+ppFppFp
+0000-000
+ppppppp
+0000-000
+ppppppp
+depth is 2
+1100-100
+pFFpFFp
+0000-000
+ppppppp
+0000-000
+ppppppp
+depth is 3
+1110-110
+FFFFFFp
+0010-010
+FppFppp
+0000-000
+ppppppp
+depth is 4
+1111-111
+FFFFFFp
+0011-011
+FppFppp
+0000-000
+ppppppp
+Locks and commands or functions:
+No :unlet after lock on dict:
+Vim(unlet):E741:
+{'a': 99, 'b': 100}
+:unlet after lock on dict item:
+did :unlet
+{'b': 100}
+filter() after lock on dict item:
+did filter()
+{'b': 100}
+map() after lock on dict:
+did map()
+{'a': 299, 'b': 300}
+No extend() after lock on dict item:
+Vim(put):E741:
+{'a': 99, 'b': 100}
+No remove() of write-protected scope-level variable:
+Vim(put):E795:
+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/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/test83-tags2 b/src/nvim/testdir/test83-tags2
deleted file mode 100644
index 7f9f21b0eb..0000000000
--- a/src/nvim/testdir/test83-tags2
+++ /dev/null
@@ -1,2 +0,0 @@
-!_TAG_FILE_ENCODING cp932 //
-‚`‚a‚b Xtags2.txt /‚`‚a‚b
diff --git a/src/nvim/testdir/test83-tags3 b/src/nvim/testdir/test83-tags3
deleted file mode 100644
index 0cb6591562..0000000000
--- a/src/nvim/testdir/test83-tags3
+++ /dev/null
@@ -1,102 +0,0 @@
-!_TAG_FILE_SORTED 1 //
-!_TAG_FILE_ENCODING cp932 //
-abc1 Xtags3.txt /‚`‚a‚b
-abc2 Xtags3.txt /‚`‚a‚b
-abc3 Xtags3.txt /‚`‚a‚b
-abc4 Xtags3.txt /‚`‚a‚b
-abc5 Xtags3.txt /‚`‚a‚b
-abc6 Xtags3.txt /‚`‚a‚b
-abc7 Xtags3.txt /‚`‚a‚b
-abc8 Xtags3.txt /‚`‚a‚b
-abc9 Xtags3.txt /‚`‚a‚b
-abc10 Xtags3.txt /‚`‚a‚b
-abc11 Xtags3.txt /‚`‚a‚b
-abc12 Xtags3.txt /‚`‚a‚b
-abc13 Xtags3.txt /‚`‚a‚b
-abc14 Xtags3.txt /‚`‚a‚b
-abc15 Xtags3.txt /‚`‚a‚b
-abc16 Xtags3.txt /‚`‚a‚b
-abc17 Xtags3.txt /‚`‚a‚b
-abc18 Xtags3.txt /‚`‚a‚b
-abc19 Xtags3.txt /‚`‚a‚b
-abc20 Xtags3.txt /‚`‚a‚b
-abc21 Xtags3.txt /‚`‚a‚b
-abc22 Xtags3.txt /‚`‚a‚b
-abc23 Xtags3.txt /‚`‚a‚b
-abc24 Xtags3.txt /‚`‚a‚b
-abc25 Xtags3.txt /‚`‚a‚b
-abc26 Xtags3.txt /‚`‚a‚b
-abc27 Xtags3.txt /‚`‚a‚b
-abc28 Xtags3.txt /‚`‚a‚b
-abc29 Xtags3.txt /‚`‚a‚b
-abc30 Xtags3.txt /‚`‚a‚b
-abc31 Xtags3.txt /‚`‚a‚b
-abc32 Xtags3.txt /‚`‚a‚b
-abc33 Xtags3.txt /‚`‚a‚b
-abc34 Xtags3.txt /‚`‚a‚b
-abc35 Xtags3.txt /‚`‚a‚b
-abc36 Xtags3.txt /‚`‚a‚b
-abc37 Xtags3.txt /‚`‚a‚b
-abc38 Xtags3.txt /‚`‚a‚b
-abc39 Xtags3.txt /‚`‚a‚b
-abc40 Xtags3.txt /‚`‚a‚b
-abc41 Xtags3.txt /‚`‚a‚b
-abc42 Xtags3.txt /‚`‚a‚b
-abc43 Xtags3.txt /‚`‚a‚b
-abc44 Xtags3.txt /‚`‚a‚b
-abc45 Xtags3.txt /‚`‚a‚b
-abc46 Xtags3.txt /‚`‚a‚b
-abc47 Xtags3.txt /‚`‚a‚b
-abc48 Xtags3.txt /‚`‚a‚b
-abc49 Xtags3.txt /‚`‚a‚b
-abc50 Xtags3.txt /‚`‚a‚b
-abc51 Xtags3.txt /‚`‚a‚b
-abc52 Xtags3.txt /‚`‚a‚b
-abc53 Xtags3.txt /‚`‚a‚b
-abc54 Xtags3.txt /‚`‚a‚b
-abc55 Xtags3.txt /‚`‚a‚b
-abc56 Xtags3.txt /‚`‚a‚b
-abc57 Xtags3.txt /‚`‚a‚b
-abc58 Xtags3.txt /‚`‚a‚b
-abc59 Xtags3.txt /‚`‚a‚b
-abc60 Xtags3.txt /‚`‚a‚b
-abc61 Xtags3.txt /‚`‚a‚b
-abc62 Xtags3.txt /‚`‚a‚b
-abc63 Xtags3.txt /‚`‚a‚b
-abc64 Xtags3.txt /‚`‚a‚b
-abc65 Xtags3.txt /‚`‚a‚b
-abc66 Xtags3.txt /‚`‚a‚b
-abc67 Xtags3.txt /‚`‚a‚b
-abc68 Xtags3.txt /‚`‚a‚b
-abc69 Xtags3.txt /‚`‚a‚b
-abc70 Xtags3.txt /‚`‚a‚b
-abc71 Xtags3.txt /‚`‚a‚b
-abc72 Xtags3.txt /‚`‚a‚b
-abc73 Xtags3.txt /‚`‚a‚b
-abc74 Xtags3.txt /‚`‚a‚b
-abc75 Xtags3.txt /‚`‚a‚b
-abc76 Xtags3.txt /‚`‚a‚b
-abc77 Xtags3.txt /‚`‚a‚b
-abc78 Xtags3.txt /‚`‚a‚b
-abc79 Xtags3.txt /‚`‚a‚b
-abc80 Xtags3.txt /‚`‚a‚b
-abc81 Xtags3.txt /‚`‚a‚b
-abc82 Xtags3.txt /‚`‚a‚b
-abc83 Xtags3.txt /‚`‚a‚b
-abc84 Xtags3.txt /‚`‚a‚b
-abc85 Xtags3.txt /‚`‚a‚b
-abc86 Xtags3.txt /‚`‚a‚b
-abc87 Xtags3.txt /‚`‚a‚b
-abc88 Xtags3.txt /‚`‚a‚b
-abc89 Xtags3.txt /‚`‚a‚b
-abc90 Xtags3.txt /‚`‚a‚b
-abc91 Xtags3.txt /‚`‚a‚b
-abc92 Xtags3.txt /‚`‚a‚b
-abc93 Xtags3.txt /‚`‚a‚b
-abc94 Xtags3.txt /‚`‚a‚b
-abc95 Xtags3.txt /‚`‚a‚b
-abc96 Xtags3.txt /‚`‚a‚b
-abc97 Xtags3.txt /‚`‚a‚b
-abc98 Xtags3.txt /‚`‚a‚b
-abc99 Xtags3.txt /‚`‚a‚b
-abc100 Xtags3.txt /‚`‚a‚b
diff --git a/src/nvim/testdir/test83.in b/src/nvim/testdir/test83.in
deleted file mode 100644
index d54b1bcddd..0000000000
--- a/src/nvim/testdir/test83.in
+++ /dev/null
@@ -1,75 +0,0 @@
-Tests for tag search with !_TAG_FILE_ENCODING.
-
-STARTTEST
-:so mbyte.vim
-:if !has('iconv') || iconv("\x82\x60", "cp932", "utf-8") != "\uff21"
-: e! test.ok
-: w! test.out
-: qa!
-:endif
-
-:/^text for tags1$/,/^text for tags1$/+1w! Xtags1.txt
-:/^text for tags2$/,/^text for tags2$/+1w! Xtags2.txt
-:/^text for tags3$/,/^text for tags3$/+1w! Xtags3.txt
-:/^tags1$/+1,/^tags1-end$/-1w! Xtags1
-
-ggdG
-
-:call setline('.', 'Results of test83')
-
-:" case1:
-:new
-:set tags=Xtags1
-:let v:errmsg = ''
-:tag abcdefghijklmnopqrs
-:if v:errmsg =~ 'E426:' || getline('.') != 'abcdefghijklmnopqrs'
-: close
-: put ='case1: failed'
-:else
-: close
-: put ='case1: ok'
-:endif
-
-:" case2:
-:new
-:set tags=test83-tags2
-:let v:errmsg = ''
-:tag /.BC
-:if v:errmsg =~ 'E426:' || getline('.') != 'ABC'
-: close
-: put ='case2: failed'
-:else
-: close
-: put ='case2: ok'
-:endif
-
-:" case3:
-:new
-:set tags=test83-tags3
-:let v:errmsg = ''
-:tag abc50
-:if v:errmsg =~ 'E426:' || getline('.') != 'ABC'
-: close
-: put ='case3: failed'
-:else
-: close
-: put ='case3: ok'
-:endif
-:close
-
-:wq! test.out
-ENDTEST
-
-text for tags1
-abcdefghijklmnopqrs
-
-text for tags2
-ABC
-
-text for tags3
-ABC
-
-tags1
-!_TAG_FILE_ENCODING utf-8 //
-abcdefghijklmnopqrs Xtags1.txt /abcdefghijklmnopqrs
-tags1-end
diff --git a/src/nvim/testdir/test83.ok b/src/nvim/testdir/test83.ok
deleted file mode 100644
index 61a1a04a18..0000000000
--- a/src/nvim/testdir/test83.ok
+++ /dev/null
@@ -1,4 +0,0 @@
-Results of test83
-case1: ok
-case2: ok
-case3: ok
diff --git a/src/nvim/testdir/test88.in b/src/nvim/testdir/test88.in
deleted file mode 100644
index c2e6a752fa..0000000000
--- a/src/nvim/testdir/test88.in
+++ /dev/null
@@ -1,88 +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
-:" 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 e29698b7bd..0000000000
--- a/src/nvim/testdir/test88.ok
+++ /dev/null
@@ -1,24 +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
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
index b05aef7f8d..5a8e580c4a 100644
--- a/src/nvim/testdir/test_breakindent.in
+++ b/src/nvim/testdir/test_breakindent.in
@@ -101,7 +101,7 @@ fygjyl:let line2 = @0
:$put =line2
:"
:let g:test="Test 14: breakindent + visual blockwise delete #1"
-:set all& breakindent
+:set all& breakindent shada+=nX-test-breakindent.shada
:30vnew
:normal! 3a1234567890
:normal! a abcde
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_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_eval.in b/src/nvim/testdir/test_eval.in
deleted file mode 100644
index b2b982a434..0000000000
--- a/src/nvim/testdir/test_eval.in
+++ /dev/null
@@ -1,223 +0,0 @@
-Test for various eval features. vim: set ft=vim :
-
-Note: system clipboard is saved, changed and restored.
-
-STARTTEST
-:so small.vim
-:set noswapfile
-:lang C
-:fun AppendRegContents(reg)
- call append('$', printf('%s: type %s; value: %s (%s), expr: %s (%s)', a:reg, getregtype(a:reg), getreg(a:reg), string(getreg(a:reg, 0, 1)), getreg(a:reg, 1), string(getreg(a:reg, 1, 1))))
-endfun
-:command -nargs=? AR :call AppendRegContents(<q-args>)
-:fun SetReg(...)
- call call('setreg', a:000)
- call append('$', printf('{{{2 setreg(%s)', string(a:000)[1:-2]))
- call AppendRegContents(a:1)
- if a:1 isnot# '='
- execute "silent normal! Go==\n==\e\"".a:1."P"
- endif
-endfun
-:fun ErrExe(str)
- call append('$', 'Executing '.a:str)
- try
- execute a:str
- catch
- $put =v:exception
- endtry
-endfun
-:fun Test()
-$put ='{{{1 let tests'
-let @" = 'abc'
-AR "
-let @" = "abc\n"
-AR "
-let @" = "abc\<C-m>"
-AR "
-let @= = '"abc"'
-AR =
-
-$put ='{{{1 Basic setreg tests'
-call SetReg('a', 'abcA', 'c')
-call SetReg('b', 'abcB', 'v')
-call SetReg('c', 'abcC', 'l')
-call SetReg('d', 'abcD', 'V')
-call SetReg('e', 'abcE', 'b')
-call SetReg('f', 'abcF', "\<C-v>")
-call SetReg('g', 'abcG', 'b10')
-call SetReg('h', 'abcH', "\<C-v>10")
-call SetReg('I', 'abcI')
-
-$put ='{{{1 Appending single lines with setreg()'
-call SetReg('A', 'abcAc', 'c')
-call SetReg('A', 'abcAl', 'l')
-call SetReg('A', 'abcAc2','c')
-call SetReg('b', 'abcBc', 'ca')
-call SetReg('b', 'abcBb', 'ba')
-call SetReg('b', 'abcBc2','ca')
-call SetReg('b', 'abcBb2','b50a')
-
-call SetReg('C', 'abcCl', 'l')
-call SetReg('C', 'abcCc', 'c')
-call SetReg('D', 'abcDb', 'b')
-
-call SetReg('E', 'abcEb', 'b')
-call SetReg('E', 'abcEl', 'l')
-call SetReg('F', 'abcFc', 'c')
-
-$put ='{{{1 Appending NL with setreg()'
-call setreg('a', 'abcA2', 'c')
-call setreg('b', 'abcB2', 'v')
-call setreg('c', 'abcC2', 'l')
-call setreg('d', 'abcD2', 'V')
-call setreg('e', 'abcE2', 'b')
-call setreg('f', 'abcF2', "\<C-v>")
-call setreg('g', 'abcG2', 'b10')
-call setreg('h', 'abcH2', "\<C-v>10")
-call setreg('I', 'abcI2')
-
-call SetReg('A', "\n")
-call SetReg('B', "\n", 'c')
-call SetReg('C', "\n")
-call SetReg('D', "\n", 'l')
-call SetReg('E', "\n")
-call SetReg('F', "\n", 'b')
-
-$put ='{{{1 Setting lists with setreg()'
-call SetReg('a', ['abcA3'], 'c')
-call SetReg('b', ['abcB3'], 'l')
-call SetReg('c', ['abcC3'], 'b')
-call SetReg('d', ['abcD3'])
-call SetReg('e', [1, 2, 'abc', 3])
-call SetReg('f', [1, 2, 3])
-
-$put ='{{{1 Appending lists with setreg()'
-call SetReg('A', ['abcA3c'], 'c')
-call SetReg('b', ['abcB3l'], 'la')
-call SetReg('C', ['abcC3b'], 'lb')
-call SetReg('D', ['abcD32'])
-
-call SetReg('A', ['abcA32'])
-call SetReg('B', ['abcB3c'], 'c')
-call SetReg('C', ['abcC3l'], 'l')
-call SetReg('D', ['abcD3b'], 'b')
-
-$put ='{{{1 Appending lists with NL with setreg()'
-call SetReg('A', ["\n", 'abcA3l2'], 'l')
-call SetReg('B', ["\n", 'abcB3c2'], 'c')
-call SetReg('C', ["\n", 'abcC3b2'], 'b')
-call SetReg('D', ["\n", 'abcD3b50'],'b50')
-
-$put ='{{{1 Setting lists with NLs with setreg()'
-call SetReg('a', ['abcA4-0', "\n", "abcA4-2\n", "\nabcA4-3", "abcA4-4\nabcA4-4-2"])
-call SetReg('b', ['abcB4c-0', "\n", "abcB4c-2\n", "\nabcB4c-3", "abcB4c-4\nabcB4c-4-2"], 'c')
-call SetReg('c', ['abcC4l-0', "\n", "abcC4l-2\n", "\nabcC4l-3", "abcC4l-4\nabcC4l-4-2"], 'l')
-call SetReg('d', ['abcD4b-0', "\n", "abcD4b-2\n", "\nabcD4b-3", "abcD4b-4\nabcD4b-4-2"], 'b')
-call SetReg('e', ['abcE4b10-0', "\n", "abcE4b10-2\n", "\nabcE4b10-3", "abcE4b10-4\nabcE4b10-4-2"], 'b10')
-
-$put ='{{{1 Search and expressions'
-call SetReg('/', ['abc/'])
-call SetReg('/', ["abc/\n"])
-call SetReg('=', ['"abc/"'])
-call SetReg('=', ["\"abc/\n\""])
-$put ='{{{1 System clipboard'
-" Save and restore system clipboard.
-" If no connection to X-Server is possible, test should succeed.
-:let _clipreg = ['+', getreg('+'), getregtype('+')]
-:let _clipopt = &cb
-:let &cb='unnamedplus'
-:1y
-:AR +
-:tabdo :windo :echo "hi"
-:3y
-:AR +
-:let &cb=_clipopt
-:call call('setreg', _clipreg)
-$put ='{{{1 Errors'
-call ErrExe('call setreg()')
-call ErrExe('call setreg(1)')
-call ErrExe('call setreg(1, 2, 3, 4)')
-call ErrExe('call setreg([], 2)')
-call ErrExe('call setreg(1, {})')
-call ErrExe('call setreg(1, 2, [])')
-call ErrExe('call setreg("/", ["1", "2"])')
-call ErrExe('call setreg("=", ["1", "2"])')
-call ErrExe('call setreg(1, ["", "", [], ""])')
-endfun
-:"
-:call Test()
-:"
-:delfunction SetReg
-:delfunction AppendRegContents
-:delfunction ErrExe
-:delfunction Test
-:delcommand AR
-:call garbagecollect(1)
-:"
-:/^start:/+1,$wq! test.out
-:" vim: et ts=4 isk-=\: fmr=???,???
-:call getchar()
-:e test.out
-:%d
-
-:" function name not starting with a capital
-:try
-: func! g:test()
-: echo "test"
-: endfunc
-:catch
-: $put =v:exception
-:endtry
-
-:" function name folowed by #
-:try
-: func! test2() "#
-: echo "test2"
-: endfunc
-:catch
-: $put =v:exception
-:endtry
-
-:" function name includes a colon
-:try
-: func! b:test()
-: echo "test"
-: endfunc
-:catch
-: $put =v:exception
-:endtry
-
-:" function name starting with/without "g:", buffer-local funcref.
-:function! g:Foo(n)
-: $put ='called Foo(' . a:n . ')'
-:endfunction
-:let b:my_func = function('Foo')
-:call b:my_func(1)
-:echo g:Foo(2)
-:echo Foo(3)
-
-:" script-local function used in Funcref must exist.
-:so test_eval_func.vim
-
-:" using $ instead of '$' must give an error
-:try
-: call append($, 'foobar')
-:catch
-: $put =v:exception
-:endtry
-
-:$put ='{{{1 getcurpos/setpos'
-/^012345678
-6l:let sp = getcurpos()
-0:call setpos('.', sp)
-jyl:$put
-
-:/^start:/+1,$wq! test.out
-:" vim: et ts=4 isk-=\: fmr=???,???
-:call getchar()
-ENDTEST
-
-012345678
-012345678
-
-start:
diff --git a/src/nvim/testdir/test_eval.ok b/src/nvim/testdir/test_eval.ok
deleted file mode 100644
index 2cdb8f5da8..0000000000
--- a/src/nvim/testdir/test_eval.ok
+++ /dev/null
Binary files differ
diff --git a/src/nvim/testdir/test_eval_func.vim b/src/nvim/testdir/test_eval_func.vim
deleted file mode 100644
index 48d01df27d..0000000000
--- a/src/nvim/testdir/test_eval_func.vim
+++ /dev/null
@@ -1,12 +0,0 @@
-" Vim script used in test_eval.in. Needed for script-local function.
-
-func! s:Testje()
- return "foo"
-endfunc
-
-let Bar = function('s:Testje')
-
-$put ='s:Testje exists: ' . exists('s:Testje')
-$put ='func s:Testje exists: ' . exists('*s:Testje')
-$put ='Bar exists: ' . exists('Bar')
-$put ='func Bar exists: ' . exists('*Bar')
diff --git a/src/nvim/testdir/test_listlbr.in b/src/nvim/testdir/test_listlbr.in
index 36235ea915..6084711786 100644
--- a/src/nvim/testdir/test_listlbr.in
+++ b/src/nvim/testdir/test_listlbr.in
@@ -24,20 +24,24 @@ STARTTEST
: $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)
@@ -51,6 +55,7 @@ STARTTEST
:$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
@@ -58,13 +63,57 @@ STARTTEST
: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.
+:"
+:let g:test ="Test 9: using redo after block visual mode"
+:$put =g:test
+Go
+aaa
+aaa
+a2k2j~e.
+:"
+:let g:test ="Test 10: using normal commands after block-visual"
+:$put =g:test
+:set linebreak
+Go
+abcd{ef
+ghijklm
+no}pqrs2k0f{c%
+:"
+:let g:test ="Test 11: using block replace mode after wrapping"
+:$put =g:test
+:set linebreak wrap
+Go150aayypk147|jr0
+:"
+:let g:test ="Test 12: set linebreak list listchars=space:_,tab:>-,tail:-,eol:$"
+:set list listchars=space:_,trail:-,tab:>-,eol:$
+:$put =g:test
+:let line="a aaaaaaaaaaaaaaaaaaaaaa\ta "
+:$put =line
+:$
+:norm! zt
+:redraw!
+:let line=ScreenChar(winwidth(0))
+:call DoRecordScreen()
:%w! test.out
:qa!
ENDTEST
diff --git a/src/nvim/testdir/test_listlbr.ok b/src/nvim/testdir/test_listlbr.ok
index ee74667661..b32a54969e 100644
--- a/src/nvim/testdir/test_listlbr.ok
+++ b/src/nvim/testdir/test_listlbr.ok
@@ -32,7 +32,31 @@ 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
+Test 9: using redo after block visual mode
+
+AaA
+AaA
+A
+Test 10: using normal commands after block-visual
+
+abcdpqrs
+Test 11: using block replace mode after wrapping
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0aaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0aaa
+Test 12: set linebreak list listchars=space:_,tab:>-,tail:-,eol:$
+a aaaaaaaaaaaaaaaaaaaaaa a
+
+Test 12: set linebreak list listchars=space:_,tab:>-,tail:-,eol:$
+a_
+aaaaaaaaaaaaaaaaaaaa
+aa>-----a-$
+~
diff --git a/src/nvim/testdir/test_viml.vim b/src/nvim/testdir/test_viml.vim
new file mode 100644
index 0000000000..9f0618bd45
--- /dev/null
+++ b/src/nvim/testdir/test_viml.vim
@@ -0,0 +1,930 @@
+" 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
+
+
+"-------------------------------------------------------------------------------
+" 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 7f7d138358..202c5666a1 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;
@@ -628,8 +628,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 +694,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;
}
@@ -845,7 +845,7 @@ static void fix_terminfo(TUIData *data)
if ((term_prog && !strcmp(term_prog, "Konsole"))
|| os_getenv("KONSOLE_DBUS_SESSION") != NULL) {
// Konsole uses a proprietary escape code to set the cursor shape
- // and does not suppport DECSCUSR.
+ // and does not support DECSCUSR.
data->unibi_ext.enter_insert_mode = (int)unibi_add_ext_str(ut, NULL,
TMUX_WRAP("\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07"));
data->unibi_ext.enter_replace_mode = (int)unibi_add_ext_str(ut, NULL,
@@ -854,13 +854,15 @@ static void fix_terminfo(TUIData *data)
TMUX_WRAP("\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07"));
} else if (!vte_version || atoi(vte_version) >= 3900) {
// Assume that the terminal supports DECSCUSR unless it is an
- // old VTE based terminal
+ // old VTE based terminal. This should not get wrapped for tmux,
+ // which will handle it via its Ss/Se terminfo extension - usually
+ // according to its terminal-overrides.
data->unibi_ext.enter_insert_mode = (int)unibi_add_ext_str(ut, NULL,
- TMUX_WRAP("\x1b[5 q"));
+ "\x1b[5 q");
data->unibi_ext.enter_replace_mode = (int)unibi_add_ext_str(ut, NULL,
- TMUX_WRAP("\x1b[3 q"));
+ "\x1b[3 q");
data->unibi_ext.exit_insert_mode = (int)unibi_add_ext_str(ut, NULL,
- TMUX_WRAP("\x1b[2 q"));
+ "\x1b[2 q");
}
end:
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 786f6026de..d32969f149 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -55,14 +55,25 @@ static int height, width;
//
// See http://stackoverflow.com/a/11172679 for a better explanation of how it
// works.
-#define UI_CALL(...) \
- do { \
- flush_cursor_update(); \
- for (size_t i = 0; i < ui_count; i++) { \
- UI *ui = uis[i]; \
- UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \
- } \
- } while (0)
+#ifdef _MSC_VER
+ #define UI_CALL(funname, ...) \
+ do { \
+ flush_cursor_update(); \
+ for (size_t i = 0; i < ui_count; i++) { \
+ UI *ui = uis[i]; \
+ UI_CALL_MORE(funname, __VA_ARGS__); \
+ } \
+ } while (0)
+#else
+ #define UI_CALL(...) \
+ do { \
+ flush_cursor_update(); \
+ for (size_t i = 0; i < ui_count; i++) { \
+ UI *ui = uis[i]; \
+ UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \
+ } \
+ } while (0)
+#endif
#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, ZERO, ignore)
#define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6
#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 69ac18ad54..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.
@@ -77,6 +77,7 @@
#include <limits.h>
#include <stdbool.h>
#include <string.h>
+#include <fcntl.h>
#include "nvim/vim.h"
#include "nvim/ascii.h"
@@ -1781,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
@@ -2221,12 +2228,17 @@ static void u_undoredo(int undo)
/*
* restore marks from before undo/redo
*/
- for (i = 0; i < NMARKS; ++i)
+ for (i = 0; i < NMARKS; ++i) {
if (curhead->uh_namedm[i].mark.lnum != 0) {
free_fmark(curbuf->b_namedm[i]);
curbuf->b_namedm[i] = curhead->uh_namedm[i];
+ }
+ if (namedm[i].mark.lnum != 0) {
curhead->uh_namedm[i] = namedm[i];
+ } else {
+ curhead->uh_namedm[i].mark.lnum = 0;
}
+ }
if (curhead->uh_visual.vi_start.lnum != 0) {
curbuf->b_visual = curhead->uh_visual;
curhead->uh_visual = visualinfo;
@@ -2864,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
@@ -2876,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 2b0d6f22f2..7ee3b596de 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -35,11 +35,6 @@
char *Version = VIM_VERSION_SHORT;
char *longVersion = NVIM_VERSION_LONG;
-char *longVersionWithDate = NVIM_VERSION_LONG \
- " (compiled " __DATE__ " " __TIME__ ")";
-#ifdef NVIM_VERSION_COMMIT
-char *version_commit = "Commit: " NVIM_VERSION_COMMIT;
-#endif
char *version_buildtype = "Build type: " NVIM_VERSION_BUILD_TYPE;
char *version_cflags = "Compilation: " NVIM_VERSION_CFLAGS;
@@ -74,275 +69,966 @@ static char *features[] = {
// clang-format off
static int included_patches[] = {
- // 922,
+ 1809,
+ 1808,
+ 1806,
+ 1799,
+ 1757,
+ 1755,
+ 1753,
+ 1654,
+ 1652,
+ 1643,
+ 1641,
+
+ // 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
+ // 1216 NA
+ // 1215 NA
+ // 1214 NA
+ // 1213 NA
+ // 1212 NA
+ // 1211 NA
+ // 1210 NA
+ // 1209 NA
+ // 1208 NA
+ // 1207 NA
+ // 1206 NA
+ // 1205 NA
+ // 1204 NA
+ // 1203 NA
+ // 1202 NA
+ // 1201 NA
+ // 1200 NA
+ // 1199 NA
+ // 1198 NA
+ // 1197 NA
+ // 1196 NA
+ // 1195 NA
+ // 1194 NA
+ // 1193 NA
+ // 1192 NA
+ // 1191 NA
+ // 1190 NA
+ // 1189 NA
+ // 1188 NA
+ // 1187 NA
+ // 1186,
+ // 1185 NA
+ // 1184 NA
+ // 1183 NA
+ // 1182 NA
+ 1181,
+ 1180,
+ // 1179,
+ 1178,
+ // 1177 NA
+ // 1176 NA
+ // 1175 NA
+ // 1174 NA
+ 1173,
+ // 1172 NA
+ // 1171 NA
+ // 1170 NA
+ // 1169 NA
+ 1168,
+ 1167,
+ 1166,
+ // 1165 NA
+ 1164,
+ 1163,
+ // 1162 NA
+ // 1161,
+ 1160,
+ // 1159 NA
+ // 1158 NA
+ 1157,
+ // 1156 NA
+ // 1155 NA
+ // 1154 NA
+ // 1153,
+ // 1152 NA
+ // 1151,
+ // 1150,
+ 1149,
+ // 1148 NA
+ // 1147,
+ // 1146 NA
+ // 1145 NA
+ 1144,
+ 1143,
+ // 1142,
+ 1141,
+ // 1140,
+ // 1139 NA
+ // 1138 NA
+ 1137,
+ // 1136,
+ // 1135 NA
+ // 1134 NA
+ // 1133 NA
+ // 1132,
+ // 1131 NA
+ // 1130,
+ // 1129 NA
+ // 1128 NA
+ // 1127 NA
+ // 1126,
+ // 1125 NA
+ // 1124 NA
+ // 1123,
+ // 1122 NA
+ // 1121,
+ 1120,
+ // 1119,
+ 1118,
+ 1117,
+ 1116,
+ // 1115 NA
+ 1114,
+ 1113,
+ 1112,
+ // 1111,
+ // 1110,
+ // 1109 NA
+ // 1108,
+ 1107,
+ // 1106 NA
+ 1105,
+ // 1104 NA
+ // 1103 NA
+ // 1102,
+ 1101,
+ // 1100 NA
+ // 1099 NA
+ // 1098 NA
+ // 1097,
+ // 1096,
+ // 1095 NA
+ // 1094,
+ 1093,
+ 1092,
+ 1091,
+ // 1090,
+ 1089,
+ 1088,
+ 1087,
+ 1086,
+ 1085,
+ 1084,
+ // 1083 NA
+ // 1082 NA
+ 1081,
+ // 1080 NA
+ // 1079,
+ // 1078 NA
+ // 1077 NA
+ 1076,
+ 1075,
+ // 1074 NA,
+ // 1073,
+ 1072,
+ // 1071,
+ // 1070 NA
+ // 1069 NA
+ // 1068,
+ // 1067 NA
+ // 1066 NA
+ 1065,
+ // 1064,
+ // 1063 NA
+ // 1062 NA
+ // 1061,
+ // 1060 NA
+ // 1059,
+ // 1058,
+ // 1057,
+ // 1056,
+ 1055,
+ 1054,
+ // 1053,
+ 1052,
+ // 1051,
+ // 1050,
+ 1049,
+ 1048,
+ 1047,
+ // 1046,
+ // 1045 NA
+ // 1044 NA
+ // 1043 NA
+ 1042,
+ 1041,
+ // 1040 NA
+ // 1039,
+ // 1038 NA
+ 1037,
+ // 1036,
+ 1035,
+ // 1034,
+ // 1033 NA
+ 1032,
+ // 1031 NA,
+ 1030,
+ 1029,
+ // 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
+ 1013,
+ // 1012 NA
+ // 1011 NA
+ // 1010 NA,
+ // 1009 NA
+ // 1008 NA
+ 1007,
+ 1006,
+ // 1005 NA,
+ // 1004 NA,
+ // 1003 NA,
+ // 1002 NA,
+ 1001,
+ 1000,
+ // 999 NA
+ 998,
+ // 997 NA
+ // 996 NA
+ // 995 NA
+ // 994 NA
+ // 993 NA
+ // 992 NA
+ 991,
+ // 990 NA
+ 989,
+ // 988 NA
+ // 987 NA
+ // 986 NA
+ // 985 NA
+ 984,
+ // 983 NA
+ // 982 NA
+ 981,
+ 980,
+ // 979 NA
+ 978,
+ 977,
+ // 976 NA
+ 975,
+ 974,
+ 973,
+ 972,
+ // 971 NA
+ // 970 NA
+ // 969 NA
+ // 968 NA
+ // 967 NA
+ // 966 NA
+ // 965 NA
+ // 964 NA
+ 963,
+ // 962 NA
+ 961,
+ // 960 NA
+ // 959 NA
+ 958,
+ 957,
+ // 956 NA
+ 955,
+ // 954 NA
+ 953,
+ 952,
+ 951,
+ 950,
+ 949,
+ // 948 NA
+ // 947 NA
+ 946,
+ 945,
+ 944,
+ // 943 NA
+ 942,
+ 941,
+ // 940 NA
+ 939,
+ // 938 NA
+ 937,
+ 936,
+ // 935 NA
+ // 934 NA
+ 933,
+ 932,
+ // 931 NA
+ // 930 NA
+ 929,
+ // 928 NA
+ // 927 NA
+ 926,
+ 925,
+ // 924 NA
+ // 923 NA
+ 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,
+ 898,
+ // 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,
- // 864,
- // 863,
+ // 866 NA
+ // 865 NA
+ // 864 NA
+ // 863 NA
// 862 NA
// 861 NA
- // 860,
- // 859,
- // 858,
- // 857,
- // 856,
+ // 860 NA
+ 859,
+ 858,
+ 857,
+ 856,
// 855 NA
- // 854,
- // 853,
+ // 854 NA
+ 853,
// 852 NA
// 851 NA
// 850 NA
849,
- // 848,
- // 847,
+ 848,
+ 847,
// 846 NA
- // 845,
- // 844,
- // 843,
- // 842,
- // 841,
+ 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,
+ 823,
+ 822,
+ // 821 NA
+ 820,
+ 819,
+ 818,
+ 817,
+ 816,
+ 815,
+ 814,
813,
- // 812,
- // 811,
- // 810,
+ // 812 NA
+ 811,
+ 810,
809,
- // 808,
- // 807,
- // 806,
- // 805,
- // 804,
- // 803,
+ // 808 NA
+ 807,
+ 806,
+ 805,
+ // 804 NA
+ 803,
802,
- // 801,
- // 800,
+ 801,
+ 800,
799,
- // 798,
- // 797,
+ 798,
+ // 797 NA
// 796 NA
- // 795,
+ 795,
// 794 NA
793,
- // 792,
+ 792,
791,
790,
789,
// 788 NA
787,
786,
- // 785,
+ 785,
784,
// 783 NA
- // 782,
+ 782,
781,
- // 780 NA
- // 779,
- // 778 NA
+ 780,
+ 779,
+ 778,
// 777 NA
776,
775,
774,
773,
// 772 NA
- // 771,
+ 771,
// 770 NA
- // 769,
- // 768,
- // 767,
+ 769,
+ 768,
+ // 767 NA
// 766 NA
- // 765,
- // 764,
+ 765,
+ 764,
// 763 NA
// 762 NA
// 761 NA
- // 760,
+ 760,
// 759 NA
- // 758,
+ 758,
// 757 NA
// 756 NA
- // 755,
- // 754,
- // 753,
- // 752,
+ 755,
+ 754,
+ 753,
+ // 752 NA
// 751 NA
// 750 NA
- // 749,
- // 748,
- // 747,
- // 746,
- // 745,
+ 749,
+ 748,
+ 747,
+ 746,
+ 745,
// 744 NA
- // 743,
- // 742,
- // 741,
- // 740,
- // 739,
+ 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,
+ 729,
// 728 NA
// 727 NA
// 726 NA
- // 725,
+ // 725 NA
// 724 NA
- // 723,
- // 722,
- // 721,
+ 723,
+ 722,
+ 721,
// 720 NA
719,
- // 718,
- // 717,
- // 716,
- // 715,
- // 714,
+ 718,
+ 717,
+ 716,
+ 715,
+ 714,
713,
712,
711,
710,
- // 709,
- // 708,
+ 709,
+ 708,
707,
706,
// 705 NA
- // 704,
+ 704,
// 703 NA
702,
// 701 NA
- // 700,
+ 700,
699,
- // 698,
- // 697,
+ 698,
+ 697,
696,
695,
- // 694,
- // 693,
+ 694,
+ 693,
// 692 NA
// 691 NA
690,
- // 689,
- // 688,
+ 689,
+ 688,
// 687 NA
686,
- // 685,
- // 684,
+ 685,
+ // 684 NA
// 683 NA
- // 682,
+ 682,
// 681 NA
- // 680,
+ 680,
// 679 NA
// 678 NA
// 677 NA
// 676 NA
- // 675,
+ 675,
// 674 NA
673,
- // 672,
- // 671,
- // 670,
+ 672,
+ 671,
+ 670,
// 669 NA
668,
667,
// 666 NA
- // 665,
+ 665,
// 664 NA
// 663 NA
- // 662,
- // 661,
+ 662,
+ // 661 NA
660,
659,
- // 658,
+ 658,
// 657 NA
- // 656,
- // 655,
- // 654,
+ 656,
+ 655,
+ 654,
653,
// 652 NA
651,
@@ -351,49 +1037,49 @@ static int included_patches[] = {
// 648 NA
// 647 NA
646,
- // 645,
+ 645,
// 644 NA
- // 643,
- // 642,
- // 641,
- // 640,
- // 639,
+ 643,
+ 642,
+ // 641 NA
+ 640,
+ 639,
// 638 NA
637,
636,
- // 635,
- // 634,
+ 635,
+ 634,
633,
// 632 NA
631,
630,
629,
- // 628,
+ // 628 NA
// 627 NA
// 626 NA
// 625 NA
- // 624,
+ // 624 NA
623,
// 622 NA
// 621 NA
- // 620,
+ // 620 NA
// 619 NA
// 618 NA
617,
- // 616,
+ 616,
615,
- // 614,
- // 613,
- // 612,
+ 614,
+ 613,
+ 612,
// 611 NA
// 610 NA
609,
- // 608,
- // 607,
+ 608,
+ // 607 NA
606,
- // 605,
- // 604,
- // 603,
+ 605,
+ 604,
+ 603,
602,
601,
600,
@@ -423,11 +1109,11 @@ static int included_patches[] = {
576,
575,
574,
- // 573,
+ 573,
572,
// 571 NA
// 570 NA
- // 569,
+ 569,
568,
567,
566,
@@ -752,13 +1438,13 @@ static int included_patches[] = {
247,
// 246 NA
245,
- // 244,
+ // 244 NA
243,
242,
241,
240,
239,
- // 238,
+ // 238 NA
237,
236,
235,
@@ -1098,10 +1784,7 @@ void list_version(void)
{
// When adding features here, don't forget to update the list of
// internal variables in eval.c!
- MSG(longVersionWithDate);
-#ifdef NVIM_VERSION_COMMIT
- MSG(version_commit);
-#endif
+ MSG(longVersion);
MSG(version_buildtype);
MSG(version_cflags);
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index fa00d9efcf..165a44a148 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -27,20 +27,21 @@ Error: configure did not run properly.Check auto/config.log.
# endif
#endif
-/* user ID of root is usually zero, but not for everybody */
-#define ROOT_UID 0
-
+#include "nvim/os/os_defs.h" /* bring lots of system header files */
-/* Can't use "PACKAGE" here, conflicts with a Perl include file. */
-#ifndef VIMPACKAGE
-# define VIMPACKAGE "vim"
-#endif
+/// length of a buffer to store a number in ASCII (64 bits binary + NUL)
+#define NUMBUFLEN 65
-#include "nvim/os/os_defs.h" /* bring lots of system header files */
+// flags for vim_str2nr()
+#define STR2NR_BIN 1
+#define STR2NR_OCT 2
+#define STR2NR_HEX 4
+#define STR2NR_ALL (STR2NR_BIN + STR2NR_OCT + STR2NR_HEX)
+#define STR2NR_FORCE 8 // only when ONE of the above is used
-#define NUMBUFLEN 30 /* length of a buffer to store a number in ASCII */
+#define MAX_TYPENR 65535
-# define MAX_TYPENR 65535
+#define ROOT_UID 0
#include "nvim/keymap.h"
#include "nvim/macros.h"
@@ -200,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 */
@@ -232,15 +224,6 @@ enum {
/* Size in bytes of the hash used in the undo file. */
#define UNDO_HASH_SIZE 32
-#ifdef HAVE_FCNTL_H
-# include <fcntl.h>
-#endif
-
-
-#ifndef O_NOFOLLOW
-# define O_NOFOLLOW 0
-#endif
-
/*
* defines to avoid typecasts from (char_u *) to (char *) and back
* (vim_strchr() and vim_strrchr() are now in alloc.c)
@@ -279,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 16ff7dfb14..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);
@@ -1044,19 +1044,21 @@ static void win_init_some(win_T *newp, win_T *oldp)
}
-/*
- * Check if "win" is a pointer to an existing window.
- */
-int win_valid(win_T *win)
+/// Check if "win" is a pointer to an existing window in the current tabpage.
+///
+/// @param win window to check
+bool win_valid(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- if (win == NULL)
- return FALSE;
+ if (win == NULL) {
+ return false;
+ }
+
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp == win) {
- return TRUE;
+ return true;
}
}
- return FALSE;
+ return false;
}
/*
@@ -1732,21 +1734,18 @@ close_windows (
shell_new_rows();
}
-/*
- * Return TRUE if the current window is the only window that exists (ignoring
- * "aucmd_win").
- * Returns FALSE if there is a window, possibly in another tab page.
- */
-static int last_window(void)
+/// Check that current window is the last one.
+///
+/// @return true if the current window is the only window that exists, false if
+/// there is another, possibly in another tab page.
+static bool last_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return one_window() && first_tabpage->tp_next == NULL;
}
-/*
- * Return TRUE if there is only one window other than "aucmd_win" in the
- * current tab page.
- */
-bool one_window(void)
+/// Check that current tab page contains no more then one window other than
+/// "aucmd_win".
+bool one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
bool seen_one = false;
@@ -1761,14 +1760,20 @@ bool one_window(void)
return true;
}
-/*
- * Close the possibly last window in a tab page.
- * Returns TRUE when the window was closed already.
- */
-static int close_last_window_tabpage(win_T *win, int free_buf, tabpage_T *prev_curtab)
+/// Close the possibly last window in a tab page.
+///
+/// @param win window to close
+/// @param free_buf whether to free the window's current buffer
+/// @param prev_curtab previous tabpage that will be closed if "win" is the
+/// last window in the tabpage
+///
+/// @return true when the window was closed already.
+static bool close_last_window_tabpage(win_T *win, bool free_buf,
+ tabpage_T *prev_curtab)
+ FUNC_ATTR_NONNULL_ARG(1)
{
if (firstwin != lastwin) {
- return FALSE;
+ return false;
}
buf_T *old_curbuf = curbuf;
@@ -1809,14 +1814,15 @@ static int close_last_window_tabpage(win_T *win, int free_buf, tabpage_T *prev_c
terminal_resize(term, 0, 0);
}
- /* Since goto_tabpage_tp above did not trigger *Enter autocommands, do
- * that now. */
- apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, FALSE, curbuf);
- apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
- apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
- if (old_curbuf != curbuf)
- apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
- return TRUE;
+ // Since goto_tabpage_tp above did not trigger *Enter autocommands, do
+ // that now.
+ apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, false, curbuf);
+ apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf);
+ apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf);
+ if (old_curbuf != curbuf) {
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf);
+ }
+ return true;
}
/*
@@ -1907,9 +1913,16 @@ int win_close(win_T *win, int free_buf)
*/
if (win->w_buffer != NULL) {
win->w_closing = true;
- close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, TRUE);
- if (win_valid(win))
+ close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true);
+ if (win_valid(win)) {
win->w_closing = false;
+ }
+
+ // Make sure curbuf is valid. It can become invalid if 'bufhidden' is
+ // "wipe".
+ if (!buf_valid(curbuf)) {
+ curbuf = firstbuf;
+ }
}
if (only_one_window() && win_valid(win) && win->w_buffer == NULL
@@ -2301,20 +2314,22 @@ static win_T *frame2win(frame_T *frp)
return frp->fr_win;
}
-/*
- * Return TRUE if frame "frp" contains window "wp".
- */
-static int frame_has_win(frame_T *frp, win_T *wp)
+/// Check that the frame "frp" contains the window "wp".
+///
+/// @param frp frame
+/// @param wp window
+static bool frame_has_win(frame_T *frp, win_T *wp)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
{
- frame_T *p;
-
- if (frp->fr_layout == FR_LEAF)
+ if (frp->fr_layout == FR_LEAF) {
return frp->fr_win == wp;
-
- for (p = frp->fr_child; p != NULL; p = p->fr_next)
- if (frame_has_win(p, wp))
- return TRUE;
- return FALSE;
+ }
+ for (frame_T *p = frp->fr_child; p != NULL; p = p->fr_next) {
+ if (frame_has_win(p, wp)) {
+ return true;
+ }
+ }
+ return false;
}
/*
@@ -2406,58 +2421,72 @@ frame_new_height (
topfrp->fr_height = height;
}
-/*
- * Return TRUE if height of frame "frp" should not be changed because of
- * the 'winfixheight' option.
- */
-static int frame_fixed_height(frame_T *frp)
+/// Return true if height of frame "frp" should not be changed because of
+/// the 'winfixheight' option.
+///
+/// @param frp frame
+///
+/// @return true if the frame has a fixed height
+static bool frame_fixed_height(frame_T *frp)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- /* frame with one window: fixed height if 'winfixheight' set. */
- if (frp->fr_win != NULL)
+ // frame with one window: fixed height if 'winfixheight' set.
+ if (frp->fr_win != NULL) {
return frp->fr_win->w_p_wfh;
-
+ }
if (frp->fr_layout == FR_ROW) {
- /* The frame is fixed height if one of the frames in the row is fixed
- * height. */
- for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
- if (frame_fixed_height(frp))
- return TRUE;
- return FALSE;
+ // The frame is fixed height if one of the frames in the row is fixed
+ // height.
+ for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) {
+ if (frame_fixed_height(frp)) {
+ return true;
+ }
+ }
+ return false;
}
- /* frp->fr_layout == FR_COL: The frame is fixed height if all of the
- * frames in the row are fixed height. */
- for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
- if (!frame_fixed_height(frp))
- return FALSE;
- return TRUE;
+ // frp->fr_layout == FR_COL: The frame is fixed height if all of the
+ // frames in the row are fixed height.
+ for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) {
+ if (!frame_fixed_height(frp)) {
+ return false;
+ }
+ }
+ return true;
}
-/*
- * Return TRUE if width of frame "frp" should not be changed because of
- * the 'winfixwidth' option.
- */
-static int frame_fixed_width(frame_T *frp)
+/// Return true if width of frame "frp" should not be changed because of
+/// the 'winfixwidth' option.
+///
+/// @param frp frame
+///
+/// @return true if the frame has a fixed width
+static bool frame_fixed_width(frame_T *frp)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- /* frame with one window: fixed width if 'winfixwidth' set. */
- if (frp->fr_win != NULL)
+ // frame with one window: fixed width if 'winfixwidth' set.
+ if (frp->fr_win != NULL) {
return frp->fr_win->w_p_wfw;
-
+ }
if (frp->fr_layout == FR_COL) {
- /* The frame is fixed width if one of the frames in the row is fixed
- * width. */
- for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
- if (frame_fixed_width(frp))
- return TRUE;
- return FALSE;
+ // The frame is fixed width if one of the frames in the row is fixed
+ // width.
+ for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) {
+ if (frame_fixed_width(frp)) {
+ return true;
+ }
+ }
+ return false;
}
- /* frp->fr_layout == FR_ROW: The frame is fixed width if all of the
- * frames in the row are fixed width. */
- for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
- if (!frame_fixed_width(frp))
- return FALSE;
- return TRUE;
+ // frp->fr_layout == FR_ROW: The frame is fixed width if all of the
+ // frames in the row are fixed width.
+ for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next) {
+ if (!frame_fixed_width(frp)) {
+ return false;
+ }
+ }
+ return true;
}
/*
@@ -2919,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;
@@ -2970,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;
}
@@ -2994,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;
}
@@ -3018,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();
@@ -3028,10 +3062,10 @@ int make_tabpages(int maxcount)
return count - todo;
}
-/*
- * Return TRUE when "tpc" points to a valid tab page.
- */
-bool valid_tabpage(tabpage_T *tpc)
+/// Check that tpc points to a valid tab page.
+///
+/// @param[in] tpc Tabpage to check.
+bool valid_tabpage(tabpage_T *tpc) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
FOR_ALL_TABS(tp) {
if (tp == tpc) {
@@ -3252,17 +3286,27 @@ void goto_tabpage_win(tabpage_T *tp, win_T *wp)
}
}
-/*
- * Move the current tab page to before tab page "nr".
- */
+// Move the current tab page to after tab page "nr".
void tabpage_move(int nr)
{
- int n = nr;
- tabpage_T *tp;
+ int n = 1;
+ tabpage_T *tp;
+ tabpage_T *tp_dst;
if (first_tabpage->tp_next == NULL)
return;
+ for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) {
+ ++n;
+ }
+
+ if (tp == curtab || (nr > 0 && tp->tp_next != NULL
+ && tp->tp_next == curtab)) {
+ return;
+ }
+
+ tp_dst = tp;
+
/* Remove the current tab page from the list of tab pages. */
if (curtab == first_tabpage)
first_tabpage = curtab->tp_next;
@@ -3275,15 +3319,13 @@ void tabpage_move(int nr)
tp->tp_next = curtab->tp_next;
}
- /* Re-insert it at the specified position. */
- if (n <= 0) {
+ // Re-insert it at the specified position.
+ if (nr <= 0) {
curtab->tp_next = first_tabpage;
first_tabpage = curtab;
} else {
- for (tp = first_tabpage; tp->tp_next != NULL && n > 1; tp = tp->tp_next)
- --n;
- curtab->tp_next = tp->tp_next;
- tp->tp_next = curtab;
+ curtab->tp_next = tp_dst->tp_next;
+ tp_dst->tp_next = curtab;
}
/* Need to redraw the tabline. Tab page contents doesn't change. */
@@ -3518,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. */
@@ -4538,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);
@@ -4573,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;
@@ -4837,17 +4892,15 @@ static void frame_add_height(frame_T *frp, int n)
*/
char_u *grab_file_name(long count, linenr_T *file_lnum)
{
+ int options = FNAME_MESS | FNAME_EXP | FNAME_REL | FNAME_UNESC;
if (VIsual_active) {
size_t len;
char_u *ptr;
if (get_visual_text(NULL, &ptr, &len) == FAIL)
return NULL;
- return find_file_name_in_path(ptr, len,
- FNAME_MESS|FNAME_EXP|FNAME_REL,
- count, curbuf->b_ffname);
+ return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
}
- return file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP|FNAME_REL, count,
- file_lnum);
+ return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
}
/*
@@ -4918,12 +4971,18 @@ file_name_in_line (
* Also allow "://" when ':' is not in 'isfname'.
*/
len = 0;
- while (vim_isfilec(ptr[len])
- || ((options & FNAME_HYP) && path_is_url((char *)ptr + len)))
- if (has_mbyte)
+ while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
+ || ((options & FNAME_HYP) && path_is_url((char *)ptr + len))) {
+ if (ptr[len] == '\\' && ptr[len + 1] == ' ') {
+ // Skip over the "\" in "\ ".
+ ++len;
+ }
+ if (has_mbyte) {
len += (size_t)(*mb_ptr2len)(ptr + len);
- else
+ } else {
++len;
+ }
+ }
/*
* If there is trailing punctuation, remove it.
@@ -5048,27 +5107,22 @@ int min_rows(void)
return total;
}
-/*
- * Return TRUE if there is only one window (in the current tab page), not
- * counting a help or preview window, unless it is the current window.
- * Does not count "aucmd_win".
- */
-int only_one_window(void)
+/// Check that there is only one window (and only one tab page), not counting a
+/// help or preview window, unless it is the current window. Does not count
+/// "aucmd_win".
+bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- int count = 0;
-
- /* If there is another tab page there always is another window. */
- if (first_tabpage->tp_next != NULL)
- return FALSE;
+ // If there is another tab page there always is another window.
+ if (first_tabpage->tp_next != NULL) {
+ return false;
+ }
+ int count = 0;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer != NULL
&& (!((wp->w_buffer->b_help && !curbuf->b_help)
- || wp->w_p_pvw
- ) || wp == curwin)
- && wp != aucmd_win
- ) {
- ++count;
+ || wp->w_p_pvw) || wp == curwin) && wp != aucmd_win) {
+ count++;
}
}
return count <= 1;
@@ -5306,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;
@@ -5369,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)
@@ -5567,38 +5625,42 @@ matchitem_T *get_match(win_T *wp, int id)
}
-/*
- * Return TRUE if "topfrp" and its children are at the right height.
- */
-static int frame_check_height(frame_T *topfrp, int height)
+/// Check that "topfrp" and its children are at the right height.
+///
+/// @param topfrp top frame pointer
+/// @param height expected height
+static bool frame_check_height(frame_T *topfrp, int height)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- frame_T *frp;
-
- if (topfrp->fr_height != height)
- return FALSE;
-
- if (topfrp->fr_layout == FR_ROW)
- for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next)
- if (frp->fr_height != height)
- return FALSE;
-
- return TRUE;
+ if (topfrp->fr_height != height) {
+ return false;
+ }
+ if (topfrp->fr_layout == FR_ROW) {
+ for (frame_T *frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) {
+ if (frp->fr_height != height) {
+ return false;
+ }
+ }
+ }
+ return true;
}
-/*
- * Return TRUE if "topfrp" and its children are at the right width.
- */
-static int frame_check_width(frame_T *topfrp, int width)
+/// Check that "topfrp" and its children are at the right width.
+///
+/// @param topfrp top frame pointer
+/// @param width expected width
+static bool frame_check_width(frame_T *topfrp, int width)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- frame_T *frp;
-
- if (topfrp->fr_width != width)
- return FALSE;
-
- if (topfrp->fr_layout == FR_COL)
- for (frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next)
- if (frp->fr_width != width)
- return FALSE;
-
- return TRUE;
+ if (topfrp->fr_width != width) {
+ return false;
+ }
+ if (topfrp->fr_layout == FR_COL) {
+ for (frame_T *frp = topfrp->fr_child; frp != NULL; frp = frp->fr_next) {
+ if (frp->fr_width != width) {
+ return false;
+ }
+ }
+ }
+ return true;
}
diff --git a/src/nvim/window.h b/src/nvim/window.h
index eccc7835a8..2ac4c00c28 100644
--- a/src/nvim/window.h
+++ b/src/nvim/window.h
@@ -10,6 +10,7 @@
#define FNAME_INCL 8 /* apply 'includeexpr' */
#define FNAME_REL 16 /* ".." and "./" are relative to the (current)
file instead of the current directory */
+#define FNAME_UNESC 32 // remove backslashes used for escaping
/*
* arguments for win_split()