aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml6
-rw-r--r--CMakeLists.txt2
-rw-r--r--config/config.h.in1
-rw-r--r--runtime/CMakeLists.txt2
-rw-r--r--runtime/autoload/tutor.vim3
-rw-r--r--runtime/doc/eval.txt3
-rw-r--r--runtime/lua/man.lua6
-rw-r--r--runtime/plugin/rplugin.vim2
-rw-r--r--runtime/spell/en.utf-8.splbin0 -> 609337 bytes
-rw-r--r--src/nvim/README.md57
-rw-r--r--src/nvim/api/buffer.c2
-rw-r--r--src/nvim/api/private/helpers.c2
-rw-r--r--src/nvim/api/vim.c50
-rw-r--r--src/nvim/arabic.c1070
-rw-r--r--src/nvim/channel.c42
-rw-r--r--src/nvim/eval.c292
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/eval/decode.c34
-rw-r--r--src/nvim/eval/typval.c99
-rw-r--r--src/nvim/eval/typval.h118
-rw-r--r--src/nvim/ex_cmds.c3
-rw-r--r--src/nvim/ex_cmds2.c6
-rw-r--r--src/nvim/ex_getln.c8
-rw-r--r--src/nvim/getchar.c23
-rw-r--r--src/nvim/keymap.c10
-rw-r--r--src/nvim/lua/converter.c7
-rw-r--r--src/nvim/main.c2
-rw-r--r--src/nvim/memory.c2
-rw-r--r--src/nvim/menu.c2
-rw-r--r--src/nvim/misc1.c9
-rw-r--r--src/nvim/normal.c2
-rw-r--r--src/nvim/ops.c25
-rw-r--r--src/nvim/os/fileio.c6
-rw-r--r--src/nvim/os/input.c2
-rw-r--r--src/nvim/quickfix.c2
-rw-r--r--src/nvim/regexp.c4
-rw-r--r--src/nvim/screen.c26
-rw-r--r--src/nvim/search.c8
-rw-r--r--src/nvim/shada.c2
-rw-r--r--src/nvim/spell.c73
-rw-r--r--src/nvim/spellfile.c2
-rw-r--r--src/nvim/syntax.c16
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/testdir/Makefile3
-rw-r--r--src/nvim/testdir/shared.vim16
-rw-r--r--src/nvim/testdir/test_arabic.vim472
-rw-r--r--src/nvim/testdir/test_cmdline.vim20
-rw-r--r--src/nvim/testdir/test_diffmode.vim95
-rw-r--r--src/nvim/testdir/test_listlbr_utf8.vim27
-rw-r--r--src/nvim/testdir/test_normal.vim6
-rw-r--r--src/nvim/testdir/test_search.vim7
-rw-r--r--src/nvim/testdir/test_spell.vim821
-rw-r--r--src/nvim/testdir/test_substitute.vim8
-rw-r--r--src/nvim/testdir/test_system.vim48
-rw-r--r--src/nvim/tui/tui.c2
-rw-r--r--src/nvim/ugrid.c1
-rw-r--r--src/nvim/undo.c29
-rw-r--r--src/nvim/version.c6
-rw-r--r--src/nvim/window.c4
-rw-r--r--test/functional/api/vim_spec.lua78
-rw-r--r--test/functional/autocmd/tabclose_spec.lua18
-rw-r--r--test/functional/autocmd/tabnewentered_spec.lua8
-rw-r--r--test/functional/eval/getline_spec.lua39
-rw-r--r--test/functional/eval/system_spec.lua10
-rw-r--r--test/functional/ex_cmds/map_spec.lua7
-rw-r--r--test/functional/ex_cmds/sign_spec.lua4
-rw-r--r--test/functional/helpers.lua14
-rw-r--r--test/functional/terminal/tui_spec.lua29
-rw-r--r--test/functional/ui/cmdline_highlight_spec.lua2
-rw-r--r--test/unit/eval/helpers.lua5
-rw-r--r--test/unit/eval/typval_spec.lua2
-rw-r--r--test/unit/keymap_spec.lua71
73 files changed, 2662 insertions, 1225 deletions
diff --git a/.travis.yml b/.travis.yml
index 09ef8bba46..9a62a8a942 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -57,13 +57,13 @@ jobs:
env: >
CLANG_SANITIZER=ASAN_UBSAN
CMAKE_FLAGS="$CMAKE_FLAGS -DPREFER_LUA=ON"
+ sudo: true
- os: linux
compiler: gcc
env: FUNCTIONALTEST=functionaltest-lua
- os: linux
- # Travis creates a cache per compiler.
- # Set a different value here to store 32-bit
- # dependencies in a separate cache.
+ # Travis creates a cache per compiler. Set a different value here to
+ # store 32-bit dependencies in a separate cache.
compiler: gcc -m32
env: BUILD_32BIT=ON
- os: osx
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fb0cc262b1..74ff32a23d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -277,6 +277,8 @@ else()
set(DEBUG 0)
endif()
+option(LOG_LIST_ACTIONS "Add list actions logging" OFF)
+
add_definitions(-DINCLUDE_GENERATED_DECLARATIONS)
if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
diff --git a/config/config.h.in b/config/config.h.in
index 962eefd7a7..410d9cc825 100644
--- a/config/config.h.in
+++ b/config/config.h.in
@@ -62,6 +62,7 @@
#ifndef UNIT_TESTING
#cmakedefine HAVE_JEMALLOC
+#cmakedefine LOG_LIST_ACTIONS
#endif
#cmakedefine HAVE_BE64TOH
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt
index d992c2cc77..0172f630cd 100644
--- a/runtime/CMakeLists.txt
+++ b/runtime/CMakeLists.txt
@@ -138,7 +138,7 @@ endforeach()
file(GLOB_RECURSE RUNTIME_FILES
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
rgb.txt
- *.vim *.lua *.dict *.py *.rb *.ps *.tutor *.tutor.json)
+ *.vim *.lua *.dict *.py *.rb *.ps *.spl *.tutor *.tutor.json)
foreach(F ${RUNTIME_FILES})
get_filename_component(BASEDIR ${F} PATH)
diff --git a/runtime/autoload/tutor.vim b/runtime/autoload/tutor.vim
index 56e2283465..0f01190b9b 100644
--- a/runtime/autoload/tutor.vim
+++ b/runtime/autoload/tutor.vim
@@ -2,9 +2,6 @@
" Setup: {{{1
function! tutor#SetupVim()
- if &columns < 90
- set columns=90
- endif
if !exists('g:did_load_ftplugin') || g:did_load_ftplugin != 1
filetype plugin on
endif
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index ae62498d35..7820b85bf4 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -7645,6 +7645,9 @@ system({cmd} [, {input}]) *system()* *E677*
|writefile()| does with {binary} set to "b" (i.e. with
a newline between each list item, and newlines inside list
items converted to NULs).
+ When {input} is given and is a valid buffer id, the content of
+ the buffer is written to the file line by line, each line
+ terminated by a NL (and NUL where the text has NL).
*E5677*
Note: system() cannot write to or read from backgrounded ("&")
shell commands, e.g.: >
diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua
index baa522f343..b0fbe9cc35 100644
--- a/runtime/lua/man.lua
+++ b/runtime/lua/man.lua
@@ -148,8 +148,8 @@ local function highlight_line(line, linenr)
end
local function highlight_man_page()
- local mod = vim.api.nvim_eval("&modifiable")
- vim.api.nvim_command("set modifiable")
+ local mod = vim.api.nvim_buf_get_option(0, "modifiable")
+ vim.api.nvim_buf_set_option(0, "modifiable", true)
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
for i, line in ipairs(lines) do
@@ -162,7 +162,7 @@ local function highlight_man_page()
end
buf_hls = {}
- vim.api.nvim_command("let &modifiable = "..mod)
+ vim.api.nvim_buf_set_option(0, "modifiable", mod)
end
return { highlight_man_page = highlight_man_page }
diff --git a/runtime/plugin/rplugin.vim b/runtime/plugin/rplugin.vim
index ca15ec82d1..56163c894e 100644
--- a/runtime/plugin/rplugin.vim
+++ b/runtime/plugin/rplugin.vim
@@ -56,6 +56,6 @@ function! s:LoadRemotePlugins() abort
endif
endfunction
-command! UpdateRemotePlugins call remote#host#UpdateRemotePlugins()
+command! -bar UpdateRemotePlugins call remote#host#UpdateRemotePlugins()
call s:LoadRemotePlugins()
diff --git a/runtime/spell/en.utf-8.spl b/runtime/spell/en.utf-8.spl
new file mode 100644
index 0000000000..83b9b8f7c2
--- /dev/null
+++ b/runtime/spell/en.utf-8.spl
Binary files differ
diff --git a/src/nvim/README.md b/src/nvim/README.md
index da87a0208e..1ece92e44d 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -1,10 +1,22 @@
-Nvim core source
-================
+Nvim core
+=========
Module-specific details are documented at the top of each module (`terminal.c`,
-`screen.c`, ...).
+`screen.c`, …).
-See `:help development` for more guidelines.
+See `:help dev` for guidelines.
+
+Filename conventions
+--------------------
+
+The source files use extensions to hint about their purpose.
+
+- `*.c`, `*.generated.c` - full C files, with all includes, etc.
+- `*.c.h` - parametrized C files, contain all necessary includes, but require
+ defining macros before actually using. Example: `typval_encode.c.h`
+- `*.h` - full headers, with all includes. Does *not* apply to `*.generated.h`.
+- `*.h.generated.h` - exported functions’ declarations.
+- `*.c.generated.h` - static functions’ declarations.
Logs
----
@@ -20,17 +32,36 @@ UI events are logged at level 0 (`DEBUG_LOG_LEVEL`).
rm -rf build/
make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0"
-Filename conventions
---------------------
+Build with ASAN
+---------------
-The source files use extensions to hint about their purpose.
+Building Nvim with Clang sanitizers (Address Sanitizer: ASan, Undefined
+Behavior Sanitizer: UBSan, Memory Sanitizer: MSan, Thread Sanitizer: TSan) is
+a good way to catch undefined behavior, leaks and other errors as soon as they
+happen. It's significantly faster than Valgrind.
-- `*.c`, `*.generated.c` - full C files, with all includes, etc.
-- `*.c.h` - parametrized C files, contain all necessary includes, but require
- defining macros before actually using. Example: `typval_encode.c.h`
-- `*.h` - full headers, with all includes. Does *not* apply to `*.generated.h`.
-- `*.h.generated.h` - exported functions’ declarations.
-- `*.c.generated.h` - static functions’ declarations.
+Requires clang 3.4 or later:
+
+ clang --version
+
+Build Nvim with sanitizer instrumentation:
+
+ CC=clang make CMAKE_EXTRA_FLAGS="-DCLANG_ASAN_UBSAN=ON"
+
+Create a directory to store logs:
+
+ mkdir -p "$HOME/logs"
+
+Enable the sanitizer(s) via these environment variables:
+
+ # Change to detect_leaks=1 to detect memory leaks (slower).
+ export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan"
+ export ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-5.0/bin/llvm-symbolizer
+
+ export MSAN_SYMBOLIZER_PATH=/usr/lib/llvm-5.0/bin/llvm-symbolizer
+ export TSAN_OPTIONS="external_symbolizer_path=/usr/lib/llvm-5.0/bin/llvm-symbolizer log_path=${HOME}/logs/tsan"
+
+Logs will be written to `${HOME}/logs/*san.PID`.
TUI debugging
-------------
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index fdde28f2bb..01dde9dd09 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -806,7 +806,7 @@ Integer nvim_buf_add_highlight(Buffer buffer,
/// 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
+/// To clear a source group in the entire buffer, pass in 0 and -1 to
/// line_start and line_end respectively.
///
/// @param buffer Buffer handle
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 26ad7ac1a6..9baa996560 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -783,7 +783,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
break;
case kObjectTypeArray: {
- list_T *const list = tv_list_alloc();
+ list_T *const list = tv_list_alloc((ptrdiff_t)obj.data.array.size);
for (uint32_t i = 0; i < obj.data.array.size; i++) {
Object item = obj.data.array.items[i];
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 172f2ce18e..0e7cc428d4 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -43,14 +43,15 @@
#endif
/// Executes an ex-command.
-/// On VimL error: Returns the VimL error; v:errmsg is not updated.
+///
+/// On parse error: forwards the Vim error; does not update v:errmsg.
+/// On runtime error: forwards the Vim error; does not update v:errmsg.
///
/// @param command Ex-command string
-/// @param[out] err Error details (including actual VimL error), if any
+/// @param[out] err Error details (Vim error), if any
void nvim_command(String command, Error *err)
FUNC_API_SINCE(1)
{
- // Run the command
try_start();
do_cmdline_cmd(command.data);
update_screen(VALID);
@@ -207,18 +208,49 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt,
return cstr_as_string(ptr);
}
-String nvim_command_output(String str, Error *err)
+/// Executes an ex-command and returns its (non-error) output.
+/// Shell |:!| output is not captured.
+///
+/// On parse error: forwards the Vim error; does not update v:errmsg.
+/// On runtime error: forwards the Vim error; does not update v:errmsg.
+///
+/// @param command Ex-command string
+/// @param[out] err Error details (Vim error), if any
+String nvim_command_output(String command, Error *err)
FUNC_API_SINCE(1)
{
- do_cmdline_cmd("redir => v:command_output");
- nvim_command(str, err);
- do_cmdline_cmd("redir END");
+ const int save_msg_silent = msg_silent;
+ garray_T *const save_capture_ga = capture_ga;
+ garray_T capture_local;
+ ga_init(&capture_local, 1, 80);
+
+ try_start();
+ msg_silent++;
+ capture_ga = &capture_local;
+ do_cmdline_cmd(command.data);
+ capture_ga = save_capture_ga;
+ msg_silent = save_msg_silent;
+ try_end(err);
if (ERROR_SET(err)) {
- return (String)STRING_INIT;
+ goto theend;
+ }
+
+ if (capture_local.ga_len > 1) {
+ // redir always(?) prepends a newline; remove it.
+ char *s = capture_local.ga_data;
+ assert(s[0] == '\n');
+ memmove(s, s + 1, (size_t)capture_local.ga_len);
+ s[capture_local.ga_len - 1] = '\0';
+ return (String) { // Caller will free the memory.
+ .data = s,
+ .size = (size_t)(capture_local.ga_len - 1),
+ };
}
- return cstr_to_string((char *)get_vim_var_str(VV_COMMAND_OUTPUT));
+theend:
+ ga_clear(&capture_local);
+ return (String)STRING_INIT;
}
/// Evaluates a VimL expression (:help expression).
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index 1ef51d2a2a..e120e6d492 100644
--- a/src/nvim/arabic.c
+++ b/src/nvim/arabic.c
@@ -393,491 +393,141 @@ static bool A_is_f(int cur_c)
// Change shape - from ISO-8859-6/Isolated to Form-B Isolated
static int chg_c_a2s(int cur_c)
{
- int tempc;
-
switch (cur_c) {
- case a_HAMZA:
- tempc = a_s_HAMZA;
- break;
-
- case a_ALEF_MADDA:
- tempc = a_s_ALEF_MADDA;
- break;
-
- case a_ALEF_HAMZA_ABOVE:
- tempc = a_s_ALEF_HAMZA_ABOVE;
- break;
-
- case a_WAW_HAMZA:
- tempc = a_s_WAW_HAMZA;
- break;
-
- case a_ALEF_HAMZA_BELOW:
- tempc = a_s_ALEF_HAMZA_BELOW;
- break;
-
- case a_YEH_HAMZA:
- tempc = a_s_YEH_HAMZA;
- break;
-
- case a_ALEF:
- tempc = a_s_ALEF;
- break;
-
- case a_TEH_MARBUTA:
- tempc = a_s_TEH_MARBUTA;
- break;
-
- case a_DAL:
- tempc = a_s_DAL;
- break;
-
- case a_THAL:
- tempc = a_s_THAL;
- break;
-
- case a_REH:
- tempc = a_s_REH;
- break;
-
- case a_ZAIN:
- tempc = a_s_ZAIN;
- break;
-
- case a_TATWEEL: // exceptions
- tempc = cur_c;
- break;
-
- case a_WAW:
- tempc = a_s_WAW;
- break;
-
- case a_ALEF_MAKSURA:
- tempc = a_s_ALEF_MAKSURA;
- break;
-
- case a_BEH:
- tempc = a_s_BEH;
- break;
-
- case a_TEH:
- tempc = a_s_TEH;
- break;
-
- case a_THEH:
- tempc = a_s_THEH;
- break;
-
- case a_JEEM:
- tempc = a_s_JEEM;
- break;
-
- case a_HAH:
- tempc = a_s_HAH;
- break;
-
- case a_KHAH:
- tempc = a_s_KHAH;
- break;
-
- case a_SEEN:
- tempc = a_s_SEEN;
- break;
-
- case a_SHEEN:
- tempc = a_s_SHEEN;
- break;
-
- case a_SAD:
- tempc = a_s_SAD;
- break;
-
- case a_DAD:
- tempc = a_s_DAD;
- break;
-
- case a_TAH:
- tempc = a_s_TAH;
- break;
-
- case a_ZAH:
- tempc = a_s_ZAH;
- break;
-
- case a_AIN:
- tempc = a_s_AIN;
- break;
-
- case a_GHAIN:
- tempc = a_s_GHAIN;
- break;
-
- case a_FEH:
- tempc = a_s_FEH;
- break;
-
- case a_QAF:
- tempc = a_s_QAF;
- break;
-
- case a_KAF:
- tempc = a_s_KAF;
- break;
-
- case a_LAM:
- tempc = a_s_LAM;
- break;
-
- case a_MEEM:
- tempc = a_s_MEEM;
- break;
-
- case a_NOON:
- tempc = a_s_NOON;
- break;
-
- case a_HEH:
- tempc = a_s_HEH;
- break;
-
- case a_YEH:
- tempc = a_s_YEH;
- break;
-
- default:
- tempc = 0;
+ case a_HAMZA: return a_s_HAMZA;
+ case a_ALEF_MADDA: return a_s_ALEF_MADDA;
+ case a_ALEF_HAMZA_ABOVE: return a_s_ALEF_HAMZA_ABOVE;
+ case a_WAW_HAMZA: return a_s_WAW_HAMZA;
+ case a_ALEF_HAMZA_BELOW: return a_s_ALEF_HAMZA_BELOW;
+ case a_YEH_HAMZA: return a_s_YEH_HAMZA;
+ case a_ALEF: return a_s_ALEF;
+ case a_TEH_MARBUTA: return a_s_TEH_MARBUTA;
+ case a_DAL: return a_s_DAL;
+ case a_THAL: return a_s_THAL;
+ case a_REH: return a_s_REH;
+ case a_ZAIN: return a_s_ZAIN;
+ case a_TATWEEL: return cur_c; // exceptions
+ case a_WAW: return a_s_WAW;
+ case a_ALEF_MAKSURA: return a_s_ALEF_MAKSURA;
+ case a_BEH: return a_s_BEH;
+ case a_TEH: return a_s_TEH;
+ case a_THEH: return a_s_THEH;
+ case a_JEEM: return a_s_JEEM;
+ case a_HAH: return a_s_HAH;
+ case a_KHAH: return a_s_KHAH;
+ case a_SEEN: return a_s_SEEN;
+ case a_SHEEN: return a_s_SHEEN;
+ case a_SAD: return a_s_SAD;
+ case a_DAD: return a_s_DAD;
+ case a_TAH: return a_s_TAH;
+ case a_ZAH: return a_s_ZAH;
+ case a_AIN: return a_s_AIN;
+ case a_GHAIN: return a_s_GHAIN;
+ case a_FEH: return a_s_FEH;
+ case a_QAF: return a_s_QAF;
+ case a_KAF: return a_s_KAF;
+ case a_LAM: return a_s_LAM;
+ case a_MEEM: return a_s_MEEM;
+ case a_NOON: return a_s_NOON;
+ case a_HEH: return a_s_HEH;
+ case a_YEH: return a_s_YEH;
}
-
- return tempc;
+ return 0;
}
// Change shape - from ISO-8859-6/Isolated to Initial
static int chg_c_a2i(int cur_c)
{
- int tempc;
-
switch (cur_c) {
- case a_YEH_HAMZA:
- tempc = a_i_YEH_HAMZA;
- break;
-
- case a_HAMZA: // exceptions
- tempc = a_s_HAMZA;
- break;
-
- case a_ALEF_MADDA: // exceptions
- tempc = a_s_ALEF_MADDA;
- break;
-
- case a_ALEF_HAMZA_ABOVE: // exceptions
- tempc = a_s_ALEF_HAMZA_ABOVE;
- break;
-
- case a_WAW_HAMZA: // exceptions
- tempc = a_s_WAW_HAMZA;
- break;
-
- case a_ALEF_HAMZA_BELOW: // exceptions
- tempc = a_s_ALEF_HAMZA_BELOW;
- break;
-
- case a_ALEF: // exceptions
- tempc = a_s_ALEF;
- break;
-
- case a_TEH_MARBUTA: // exceptions
- tempc = a_s_TEH_MARBUTA;
- break;
-
- case a_DAL: // exceptions
- tempc = a_s_DAL;
- break;
-
- case a_THAL: // exceptions
- tempc = a_s_THAL;
- break;
-
- case a_REH: // exceptions
- tempc = a_s_REH;
- break;
-
- case a_ZAIN: // exceptions
- tempc = a_s_ZAIN;
- break;
-
- case a_TATWEEL: // exceptions
- tempc = cur_c;
- break;
-
- case a_WAW: // exceptions
- tempc = a_s_WAW;
- break;
-
- case a_ALEF_MAKSURA: // exceptions
- tempc = a_s_ALEF_MAKSURA;
- break;
-
- case a_BEH:
- tempc = a_i_BEH;
- break;
-
- case a_TEH:
- tempc = a_i_TEH;
- break;
-
- case a_THEH:
- tempc = a_i_THEH;
- break;
-
- case a_JEEM:
- tempc = a_i_JEEM;
- break;
-
- case a_HAH:
- tempc = a_i_HAH;
- break;
-
- case a_KHAH:
- tempc = a_i_KHAH;
- break;
-
- case a_SEEN:
- tempc = a_i_SEEN;
- break;
-
- case a_SHEEN:
- tempc = a_i_SHEEN;
- break;
-
- case a_SAD:
- tempc = a_i_SAD;
- break;
-
- case a_DAD:
- tempc = a_i_DAD;
- break;
-
- case a_TAH:
- tempc = a_i_TAH;
- break;
-
- case a_ZAH:
- tempc = a_i_ZAH;
- break;
-
- case a_AIN:
- tempc = a_i_AIN;
- break;
-
- case a_GHAIN:
- tempc = a_i_GHAIN;
- break;
-
- case a_FEH:
- tempc = a_i_FEH;
- break;
-
- case a_QAF:
- tempc = a_i_QAF;
- break;
-
- case a_KAF:
- tempc = a_i_KAF;
- break;
-
- case a_LAM:
- tempc = a_i_LAM;
- break;
-
- case a_MEEM:
- tempc = a_i_MEEM;
- break;
-
- case a_NOON:
- tempc = a_i_NOON;
- break;
-
- case a_HEH:
- tempc = a_i_HEH;
- break;
-
- case a_YEH:
- tempc = a_i_YEH;
- break;
-
- default:
- tempc = 0;
+ case a_YEH_HAMZA: return a_i_YEH_HAMZA;
+ case a_HAMZA: return a_s_HAMZA; // exceptions
+ case a_ALEF_MADDA: return a_s_ALEF_MADDA; // exceptions
+ case a_ALEF_HAMZA_ABOVE: return a_s_ALEF_HAMZA_ABOVE; // exceptions
+ case a_WAW_HAMZA: return a_s_WAW_HAMZA; // exceptions
+ case a_ALEF_HAMZA_BELOW: return a_s_ALEF_HAMZA_BELOW; // exceptions
+ case a_ALEF: return a_s_ALEF; // exceptions
+ case a_TEH_MARBUTA: return a_s_TEH_MARBUTA; // exceptions
+ case a_DAL: return a_s_DAL; // exceptions
+ case a_THAL: return a_s_THAL; // exceptions
+ case a_REH: return a_s_REH; // exceptions
+ case a_ZAIN: return a_s_ZAIN; // exceptions
+ case a_TATWEEL: return cur_c; // exceptions
+ case a_WAW: return a_s_WAW; // exceptions
+ case a_ALEF_MAKSURA: return a_s_ALEF_MAKSURA; // exceptions
+ case a_BEH: return a_i_BEH;
+ case a_TEH: return a_i_TEH;
+ case a_THEH: return a_i_THEH;
+ case a_JEEM: return a_i_JEEM;
+ case a_HAH: return a_i_HAH;
+ case a_KHAH: return a_i_KHAH;
+ case a_SEEN: return a_i_SEEN;
+ case a_SHEEN: return a_i_SHEEN;
+ case a_SAD: return a_i_SAD;
+ case a_DAD: return a_i_DAD;
+ case a_TAH: return a_i_TAH;
+ case a_ZAH: return a_i_ZAH;
+ case a_AIN: return a_i_AIN;
+ case a_GHAIN: return a_i_GHAIN;
+ case a_FEH: return a_i_FEH;
+ case a_QAF: return a_i_QAF;
+ case a_KAF: return a_i_KAF;
+ case a_LAM: return a_i_LAM;
+ case a_MEEM: return a_i_MEEM;
+ case a_NOON: return a_i_NOON;
+ case a_HEH: return a_i_HEH;
+ case a_YEH: return a_i_YEH;
}
-
- return tempc;
+ return 0;
}
// Change shape - from ISO-8859-6/Isolated to Medial
static int chg_c_a2m(int cur_c)
{
- int tempc;
-
switch (cur_c) {
- case a_HAMZA: // exception
- tempc = a_s_HAMZA;
- break;
-
- case a_ALEF_MADDA: // exception
- tempc = a_f_ALEF_MADDA;
- break;
-
- case a_ALEF_HAMZA_ABOVE: // exception
- tempc = a_f_ALEF_HAMZA_ABOVE;
- break;
-
- case a_WAW_HAMZA: // exception
- tempc = a_f_WAW_HAMZA;
- break;
-
- case a_ALEF_HAMZA_BELOW: // exception
- tempc = a_f_ALEF_HAMZA_BELOW;
- break;
-
- case a_YEH_HAMZA:
- tempc = a_m_YEH_HAMZA;
- break;
-
- case a_ALEF: // exception
- tempc = a_f_ALEF;
- break;
-
- case a_BEH:
- tempc = a_m_BEH;
- break;
-
- case a_TEH_MARBUTA: // exception
- tempc = a_f_TEH_MARBUTA;
- break;
-
- case a_TEH:
- tempc = a_m_TEH;
- break;
-
- case a_THEH:
- tempc = a_m_THEH;
- break;
-
- case a_JEEM:
- tempc = a_m_JEEM;
- break;
-
- case a_HAH:
- tempc = a_m_HAH;
- break;
-
- case a_KHAH:
- tempc = a_m_KHAH;
- break;
-
- case a_DAL: // exception
- tempc = a_f_DAL;
- break;
-
- case a_THAL: // exception
- tempc = a_f_THAL;
- break;
-
- case a_REH: // exception
- tempc = a_f_REH;
- break;
-
- case a_ZAIN: // exception
- tempc = a_f_ZAIN;
- break;
-
- case a_SEEN:
- tempc = a_m_SEEN;
- break;
-
- case a_SHEEN:
- tempc = a_m_SHEEN;
- break;
-
- case a_SAD:
- tempc = a_m_SAD;
- break;
-
- case a_DAD:
- tempc = a_m_DAD;
- break;
-
- case a_TAH:
- tempc = a_m_TAH;
- break;
-
- case a_ZAH:
- tempc = a_m_ZAH;
- break;
-
- case a_AIN:
- tempc = a_m_AIN;
- break;
-
- case a_GHAIN:
- tempc = a_m_GHAIN;
- break;
-
- case a_TATWEEL: // exception
- tempc = cur_c;
- break;
-
- case a_FEH:
- tempc = a_m_FEH;
- break;
-
- case a_QAF:
- tempc = a_m_QAF;
- break;
-
- case a_KAF:
- tempc = a_m_KAF;
- break;
-
- case a_LAM:
- tempc = a_m_LAM;
- break;
-
- case a_MEEM:
- tempc = a_m_MEEM;
- break;
-
- case a_NOON:
- tempc = a_m_NOON;
- break;
-
- case a_HEH:
- tempc = a_m_HEH;
- break;
-
- case a_WAW: // exception
- tempc = a_f_WAW;
- break;
-
- case a_ALEF_MAKSURA: // exception
- tempc = a_f_ALEF_MAKSURA;
- break;
-
- case a_YEH:
- tempc = a_m_YEH;
- break;
-
- default:
- tempc = 0;
+ case a_HAMZA: return a_s_HAMZA; // exception
+ case a_ALEF_MADDA: return a_f_ALEF_MADDA; // exception
+ case a_ALEF_HAMZA_ABOVE: return a_f_ALEF_HAMZA_ABOVE; // exception
+ case a_WAW_HAMZA: return a_f_WAW_HAMZA; // exception
+ case a_ALEF_HAMZA_BELOW: return a_f_ALEF_HAMZA_BELOW; // exception
+ case a_YEH_HAMZA: return a_m_YEH_HAMZA;
+ case a_ALEF: return a_f_ALEF; // exception
+ case a_BEH: return a_m_BEH;
+ case a_TEH_MARBUTA: return a_f_TEH_MARBUTA; // exception
+ case a_TEH: return a_m_TEH;
+ case a_THEH: return a_m_THEH;
+ case a_JEEM: return a_m_JEEM;
+ case a_HAH: return a_m_HAH;
+ case a_KHAH: return a_m_KHAH;
+ case a_DAL: return a_f_DAL; // exception
+ case a_THAL: return a_f_THAL; // exception
+ case a_REH: return a_f_REH; // exception
+ case a_ZAIN: return a_f_ZAIN; // exception
+ case a_SEEN: return a_m_SEEN;
+ case a_SHEEN: return a_m_SHEEN;
+ case a_SAD: return a_m_SAD;
+ case a_DAD: return a_m_DAD;
+ case a_TAH: return a_m_TAH;
+ case a_ZAH: return a_m_ZAH;
+ case a_AIN: return a_m_AIN;
+ case a_GHAIN: return a_m_GHAIN;
+ case a_TATWEEL: return cur_c; // exception
+ case a_FEH: return a_m_FEH;
+ case a_QAF: return a_m_QAF;
+ case a_KAF: return a_m_KAF;
+ case a_LAM: return a_m_LAM;
+ case a_MEEM: return a_m_MEEM;
+ case a_NOON: return a_m_NOON;
+ case a_HEH: return a_m_HEH;
+ case a_WAW: return a_f_WAW; // exception
+ case a_ALEF_MAKSURA: return a_f_ALEF_MAKSURA; // exception
+ case a_YEH: return a_m_YEH;
}
-
- return tempc;
+ return 0;
}
// Change shape - from ISO-8859-6/Isolated to final
static int chg_c_a2f(int cur_c)
{
- int tempc;
-
// NOTE: these encodings need to be accounted for
//
// a_f_ALEF_MADDA;
@@ -888,280 +538,87 @@ static int chg_c_a2f(int cur_c)
// a_f_LAM_ALEF_HAMZA_BELOW;
switch (cur_c) {
- case a_HAMZA: // exception
- tempc = a_s_HAMZA;
- break;
-
- case a_ALEF_MADDA:
- tempc = a_f_ALEF_MADDA;
- break;
-
- case a_ALEF_HAMZA_ABOVE:
- tempc = a_f_ALEF_HAMZA_ABOVE;
- break;
-
- case a_WAW_HAMZA:
- tempc = a_f_WAW_HAMZA;
- break;
-
- case a_ALEF_HAMZA_BELOW:
- tempc = a_f_ALEF_HAMZA_BELOW;
- break;
-
- case a_YEH_HAMZA:
- tempc = a_f_YEH_HAMZA;
- break;
-
- case a_ALEF:
- tempc = a_f_ALEF;
- break;
-
- case a_BEH:
- tempc = a_f_BEH;
- break;
-
- case a_TEH_MARBUTA:
- tempc = a_f_TEH_MARBUTA;
- break;
-
- case a_TEH:
- tempc = a_f_TEH;
- break;
-
- case a_THEH:
- tempc = a_f_THEH;
- break;
-
- case a_JEEM:
- tempc = a_f_JEEM;
- break;
-
- case a_HAH:
- tempc = a_f_HAH;
- break;
-
- case a_KHAH:
- tempc = a_f_KHAH;
- break;
-
- case a_DAL:
- tempc = a_f_DAL;
- break;
-
- case a_THAL:
- tempc = a_f_THAL;
- break;
-
- case a_REH:
- tempc = a_f_REH;
- break;
-
- case a_ZAIN:
- tempc = a_f_ZAIN;
- break;
-
- case a_SEEN:
- tempc = a_f_SEEN;
- break;
-
- case a_SHEEN:
- tempc = a_f_SHEEN;
- break;
-
- case a_SAD:
- tempc = a_f_SAD;
- break;
-
- case a_DAD:
- tempc = a_f_DAD;
- break;
-
- case a_TAH:
- tempc = a_f_TAH;
- break;
-
- case a_ZAH:
- tempc = a_f_ZAH;
- break;
-
- case a_AIN:
- tempc = a_f_AIN;
- break;
-
- case a_GHAIN:
- tempc = a_f_GHAIN;
- break;
-
- case a_TATWEEL: // exception
- tempc = cur_c;
- break;
-
- case a_FEH:
- tempc = a_f_FEH;
- break;
-
- case a_QAF:
- tempc = a_f_QAF;
- break;
-
- case a_KAF:
- tempc = a_f_KAF;
- break;
-
- case a_LAM:
- tempc = a_f_LAM;
- break;
-
- case a_MEEM:
- tempc = a_f_MEEM;
- break;
-
- case a_NOON:
- tempc = a_f_NOON;
- break;
-
- case a_HEH:
- tempc = a_f_HEH;
- break;
-
- case a_WAW:
- tempc = a_f_WAW;
- break;
-
- case a_ALEF_MAKSURA:
- tempc = a_f_ALEF_MAKSURA;
- break;
-
- case a_YEH:
- tempc = a_f_YEH;
- break;
-
- default:
- tempc = 0;
+ case a_HAMZA: return a_s_HAMZA; // exception
+ case a_ALEF_MADDA: return a_f_ALEF_MADDA;
+ case a_ALEF_HAMZA_ABOVE: return a_f_ALEF_HAMZA_ABOVE;
+ case a_WAW_HAMZA: return a_f_WAW_HAMZA;
+ case a_ALEF_HAMZA_BELOW: return a_f_ALEF_HAMZA_BELOW;
+ case a_YEH_HAMZA: return a_f_YEH_HAMZA;
+ case a_ALEF: return a_f_ALEF;
+ case a_BEH: return a_f_BEH;
+ case a_TEH_MARBUTA: return a_f_TEH_MARBUTA;
+ case a_TEH: return a_f_TEH;
+ case a_THEH: return a_f_THEH;
+ case a_JEEM: return a_f_JEEM;
+ case a_HAH: return a_f_HAH;
+ case a_KHAH: return a_f_KHAH;
+ case a_DAL: return a_f_DAL;
+ case a_THAL: return a_f_THAL;
+ case a_REH: return a_f_REH;
+ case a_ZAIN: return a_f_ZAIN;
+ case a_SEEN: return a_f_SEEN;
+ case a_SHEEN: return a_f_SHEEN;
+ case a_SAD: return a_f_SAD;
+ case a_DAD: return a_f_DAD;
+ case a_TAH: return a_f_TAH;
+ case a_ZAH: return a_f_ZAH;
+ case a_AIN: return a_f_AIN;
+ case a_GHAIN: return a_f_GHAIN;
+ case a_TATWEEL: return cur_c; // exception
+ case a_FEH: return a_f_FEH;
+ case a_QAF: return a_f_QAF;
+ case a_KAF: return a_f_KAF;
+ case a_LAM: return a_f_LAM;
+ case a_MEEM: return a_f_MEEM;
+ case a_NOON: return a_f_NOON;
+ case a_HEH: return a_f_HEH;
+ case a_WAW: return a_f_WAW;
+ case a_ALEF_MAKSURA: return a_f_ALEF_MAKSURA;
+ case a_YEH: return a_f_YEH;
}
-
- return tempc;
+ return 0;
}
// Change shape - from Initial to Medial
static int chg_c_i2m(int cur_c)
{
- int tempc;
-
switch (cur_c) {
- case a_i_YEH_HAMZA:
- tempc = a_m_YEH_HAMZA;
- break;
-
- case a_i_BEH:
- tempc = a_m_BEH;
- break;
-
- case a_i_TEH:
- tempc = a_m_TEH;
- break;
-
- case a_i_THEH:
- tempc = a_m_THEH;
- break;
-
- case a_i_JEEM:
- tempc = a_m_JEEM;
- break;
-
- case a_i_HAH:
- tempc = a_m_HAH;
- break;
-
- case a_i_KHAH:
- tempc = a_m_KHAH;
- break;
-
- case a_i_SEEN:
- tempc = a_m_SEEN;
- break;
-
- case a_i_SHEEN:
- tempc = a_m_SHEEN;
- break;
-
- case a_i_SAD:
- tempc = a_m_SAD;
- break;
-
- case a_i_DAD:
- tempc = a_m_DAD;
- break;
-
- case a_i_TAH:
- tempc = a_m_TAH;
- break;
-
- case a_i_ZAH:
- tempc = a_m_ZAH;
- break;
-
- case a_i_AIN:
- tempc = a_m_AIN;
- break;
-
- case a_i_GHAIN:
- tempc = a_m_GHAIN;
- break;
-
- case a_i_FEH:
- tempc = a_m_FEH;
- break;
-
- case a_i_QAF:
- tempc = a_m_QAF;
- break;
-
- case a_i_KAF:
- tempc = a_m_KAF;
- break;
-
- case a_i_LAM:
- tempc = a_m_LAM;
- break;
-
- case a_i_MEEM:
- tempc = a_m_MEEM;
- break;
-
- case a_i_NOON:
- tempc = a_m_NOON;
- break;
-
- case a_i_HEH:
- tempc = a_m_HEH;
- break;
-
- case a_i_YEH:
- tempc = a_m_YEH;
- break;
-
- default:
- tempc = 0;
+ case a_i_YEH_HAMZA: return a_m_YEH_HAMZA;
+ case a_i_BEH: return a_m_BEH;
+ case a_i_TEH: return a_m_TEH;
+ case a_i_THEH: return a_m_THEH;
+ case a_i_JEEM: return a_m_JEEM;
+ case a_i_HAH: return a_m_HAH;
+ case a_i_KHAH: return a_m_KHAH;
+ case a_i_SEEN: return a_m_SEEN;
+ case a_i_SHEEN: return a_m_SHEEN;
+ case a_i_SAD: return a_m_SAD;
+ case a_i_DAD: return a_m_DAD;
+ case a_i_TAH: return a_m_TAH;
+ case a_i_ZAH: return a_m_ZAH;
+ case a_i_AIN: return a_m_AIN;
+ case a_i_GHAIN: return a_m_GHAIN;
+ case a_i_FEH: return a_m_FEH;
+ case a_i_QAF: return a_m_QAF;
+ case a_i_KAF: return a_m_KAF;
+ case a_i_LAM: return a_m_LAM;
+ case a_i_MEEM: return a_m_MEEM;
+ case a_i_NOON: return a_m_NOON;
+ case a_i_HEH: return a_m_HEH;
+ case a_i_YEH: return a_m_YEH;
}
-
- return tempc;
+ return 0;
}
// Change shape - from Final to Medial
static int chg_c_f2m(int cur_c)
{
- int tempc;
-
switch (cur_c) {
// NOTE: these encodings are multi-positional, no ?
// case a_f_ALEF_MADDA:
// case a_f_ALEF_HAMZA_ABOVE:
// case a_f_ALEF_HAMZA_BELOW:
- case a_f_YEH_HAMZA:
- tempc = a_m_YEH_HAMZA;
- break;
-
+ case a_f_YEH_HAMZA: return a_m_YEH_HAMZA;
case a_f_WAW_HAMZA: // exceptions
case a_f_ALEF:
case a_f_TEH_MARBUTA:
@@ -1171,165 +628,60 @@ static int chg_c_f2m(int cur_c)
case a_f_ZAIN:
case a_f_WAW:
case a_f_ALEF_MAKSURA:
- tempc = cur_c;
- break;
-
- case a_f_BEH:
- tempc = a_m_BEH;
- break;
-
- case a_f_TEH:
- tempc = a_m_TEH;
- break;
-
- case a_f_THEH:
- tempc = a_m_THEH;
- break;
-
- case a_f_JEEM:
- tempc = a_m_JEEM;
- break;
-
- case a_f_HAH:
- tempc = a_m_HAH;
- break;
-
- case a_f_KHAH:
- tempc = a_m_KHAH;
- break;
-
- case a_f_SEEN:
- tempc = a_m_SEEN;
- break;
-
- case a_f_SHEEN:
- tempc = a_m_SHEEN;
- break;
-
- case a_f_SAD:
- tempc = a_m_SAD;
- break;
-
- case a_f_DAD:
- tempc = a_m_DAD;
- break;
-
- case a_f_TAH:
- tempc = a_m_TAH;
- break;
-
- case a_f_ZAH:
- tempc = a_m_ZAH;
- break;
-
- case a_f_AIN:
- tempc = a_m_AIN;
- break;
-
- case a_f_GHAIN:
- tempc = a_m_GHAIN;
- break;
-
- case a_f_FEH:
- tempc = a_m_FEH;
- break;
-
- case a_f_QAF:
- tempc = a_m_QAF;
- break;
-
- case a_f_KAF:
- tempc = a_m_KAF;
- break;
-
- case a_f_LAM:
- tempc = a_m_LAM;
- break;
-
- case a_f_MEEM:
- tempc = a_m_MEEM;
- break;
-
- case a_f_NOON:
- tempc = a_m_NOON;
- break;
-
- case a_f_HEH:
- tempc = a_m_HEH;
- break;
-
- case a_f_YEH:
- tempc = a_m_YEH;
- break;
-
+ return cur_c;
+ case a_f_BEH: return a_m_BEH;
+ case a_f_TEH: return a_m_TEH;
+ case a_f_THEH: return a_m_THEH;
+ case a_f_JEEM: return a_m_JEEM;
+ case a_f_HAH: return a_m_HAH;
+ case a_f_KHAH: return a_m_KHAH;
+ case a_f_SEEN: return a_m_SEEN;
+ case a_f_SHEEN: return a_m_SHEEN;
+ case a_f_SAD: return a_m_SAD;
+ case a_f_DAD: return a_m_DAD;
+ case a_f_TAH: return a_m_TAH;
+ case a_f_ZAH: return a_m_ZAH;
+ case a_f_AIN: return a_m_AIN;
+ case a_f_GHAIN: return a_m_GHAIN;
+ case a_f_FEH: return a_m_FEH;
+ case a_f_QAF: return a_m_QAF;
+ case a_f_KAF: return a_m_KAF;
+ case a_f_LAM: return a_m_LAM;
+ case a_f_MEEM: return a_m_MEEM;
+ case a_f_NOON: return a_m_NOON;
+ case a_f_HEH: return a_m_HEH;
+ case a_f_YEH: return a_m_YEH;
// NOTE: these encodings are multi-positional, no ?
// case a_f_LAM_ALEF_MADDA_ABOVE:
// case a_f_LAM_ALEF_HAMZA_ABOVE:
// case a_f_LAM_ALEF_HAMZA_BELOW:
// case a_f_LAM_ALEF:
- default:
- tempc = 0;
}
-
- return tempc;
+ return 0;
}
// Change shape - from Combination (2 char) to an Isolated.
static int chg_c_laa2i(int hid_c)
{
- int tempc;
-
switch (hid_c) {
- case a_ALEF_MADDA:
- tempc = a_s_LAM_ALEF_MADDA_ABOVE;
- break;
-
- case a_ALEF_HAMZA_ABOVE:
- tempc = a_s_LAM_ALEF_HAMZA_ABOVE;
- break;
-
- case a_ALEF_HAMZA_BELOW:
- tempc = a_s_LAM_ALEF_HAMZA_BELOW;
- break;
-
- case a_ALEF:
- tempc = a_s_LAM_ALEF;
- break;
-
- default:
- tempc = 0;
+ case a_ALEF_MADDA: return a_s_LAM_ALEF_MADDA_ABOVE;
+ case a_ALEF_HAMZA_ABOVE: return a_s_LAM_ALEF_HAMZA_ABOVE;
+ case a_ALEF_HAMZA_BELOW: return a_s_LAM_ALEF_HAMZA_BELOW;
+ case a_ALEF: return a_s_LAM_ALEF;
}
-
- return tempc;
+ return 0;
}
// Change shape - from Combination-Isolated to Final.
static int chg_c_laa2f(int hid_c)
{
- int tempc;
-
switch (hid_c) {
- case a_ALEF_MADDA:
- tempc = a_f_LAM_ALEF_MADDA_ABOVE;
- break;
-
- case a_ALEF_HAMZA_ABOVE:
- tempc = a_f_LAM_ALEF_HAMZA_ABOVE;
- break;
-
- case a_ALEF_HAMZA_BELOW:
- tempc = a_f_LAM_ALEF_HAMZA_BELOW;
- break;
-
- case a_ALEF:
- tempc = a_f_LAM_ALEF;
- break;
-
- default:
- tempc = 0;
+ case a_ALEF_MADDA: return a_f_LAM_ALEF_MADDA_ABOVE;
+ case a_ALEF_HAMZA_ABOVE: return a_f_LAM_ALEF_HAMZA_ABOVE;
+ case a_ALEF_HAMZA_BELOW: return a_f_LAM_ALEF_HAMZA_BELOW;
+ case a_ALEF: return a_f_LAM_ALEF;
}
-
- return tempc;
+ return 0;
}
// Do "half-shaping" on character "c". Return zero if no shaping.
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index efef95de01..265d4d8b89 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -4,6 +4,7 @@
#include "nvim/api/ui.h"
#include "nvim/channel.h"
#include "nvim/eval.h"
+#include "nvim/eval/encode.h"
#include "nvim/event/socket.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
@@ -522,32 +523,21 @@ err:
return 0;
}
-/// NB: mutates buf in place!
-static list_T *buffer_to_tv_list(char *buf, size_t count)
-{
- list_T *ret = tv_list_alloc();
- char *ptr = buf;
- size_t remaining = count;
- size_t off = 0;
-
- while (off < remaining) {
- // append the line
- if (ptr[off] == NL) {
- tv_list_append_string(ret, ptr, (ssize_t)off);
- size_t skip = off + 1;
- ptr += skip;
- remaining -= skip;
- off = 0;
- continue;
- }
- if (ptr[off] == NUL) {
- // Translate NUL to NL
- ptr[off] = NL;
- }
- off++;
- }
- tv_list_append_string(ret, ptr, (ssize_t)off);
- return ret;
+/// Convert binary byte array to a readfile()-style list
+///
+/// @param[in] buf Array to convert.
+/// @param[in] len Array length.
+///
+/// @return [allocated] Converted list.
+static inline list_T *buffer_to_tv_list(const char *const buf, const size_t len)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE
+{
+ list_T *const l = tv_list_alloc(kListLenMayKnow);
+ // Empty buffer should be represented by [''], encode_list_write() thinks
+ // empty list is fine for the case.
+ tv_list_append_string(l, "", 0);
+ encode_list_write(l, buf, len);
+ return l;
}
// vimscript job callbacks must be executed on Nvim main loop
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index f34b6db8d8..28c590c0b4 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -402,7 +402,6 @@ static struct vimvar {
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),
@@ -569,7 +568,7 @@ void eval_init(void)
dict_T *const msgpack_types_dict = tv_dict_alloc();
for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) {
- list_T *const type_list = tv_list_alloc();
+ list_T *const type_list = tv_list_alloc(0);
tv_list_set_lock(type_list, VAR_FIXED);
tv_list_ref(type_list);
dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]);
@@ -592,7 +591,7 @@ void eval_init(void)
dict_T *v_event = tv_dict_alloc();
v_event->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_EVENT, v_event);
- set_vim_var_list(VV_ERRORS, tv_list_alloc());
+ set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown));
set_vim_var_nr(VV_STDERR, CHAN_STDERR);
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
@@ -1547,6 +1546,7 @@ ex_let_vars (
assert(l != NULL);
listitem_T *item = tv_list_first(l);
+ size_t rest_len = tv_list_len(l);
while (*arg != ']') {
arg = skipwhite(arg + 1);
arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]",
@@ -1554,13 +1554,14 @@ ex_let_vars (
if (arg == NULL) {
return FAIL;
}
+ rest_len--;
item = TV_LIST_ITEM_NEXT(l, item);
arg = skipwhite(arg);
if (*arg == ';') {
/* Put the rest of the list (may be empty) in the var after ';'.
* Create a new list for this. */
- list_T *const rest_list = tv_list_alloc();
+ list_T *const rest_list = tv_list_alloc(rest_len);
while (item != NULL) {
tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item));
item = TV_LIST_ITEM_NEXT(l, item);
@@ -4513,7 +4514,7 @@ eval_index (
if (!empty2 && (n2 < 0 || n2 + 1 < n1)) {
n2 = -1;
}
- l = tv_list_alloc();
+ l = tv_list_alloc(n2 - n1 + 1);
item = tv_list_find(rettv->vval.v_list, n1);
while (n1++ <= n2) {
tv_list_append_tv(l, TV_LIST_ITEM_TV(item));
@@ -4871,7 +4872,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
list_T *l = NULL;
if (evaluate) {
- l = tv_list_alloc();
+ l = tv_list_alloc(kListLenShouldKnow);
}
*arg = skipwhite(*arg + 1);
@@ -6667,7 +6668,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
rettv->v_type = VAR_STRING;
} else {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, ARGCOUNT);
for (idx = 0; idx < ARGCOUNT; idx++) {
tv_list_append_string(rettv->vval.v_list,
(const char *)alist_name(&ARGLIST[idx]), -1);
@@ -6777,7 +6778,7 @@ static void assert_error(garray_T *gap)
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, tv_list_alloc());
+ set_vim_var_list(VV_ERRORS, tv_list_alloc(1));
}
tv_list_append_string(vimvars[VV_ERRORS].vv_list,
(const char *)gap->ga_data, (ptrdiff_t)gap->ga_len);
@@ -8117,8 +8118,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ga_append(capture_ga, NUL);
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave(capture_ga->ga_data);
- ga_clear(capture_ga);
+ rettv->vval.v_string = capture_ga->ga_data;
capture_ga = save_capture_ga;
}
@@ -8227,7 +8227,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL);
emsg_off--;
if (rettv->v_type == VAR_LIST) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, (result != NULL));
if (result != NULL) {
tv_list_append_string(rettv->vval.v_list, (const char *)result, -1);
}
@@ -8250,8 +8250,8 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options,
WILD_ALL);
} else {
- tv_list_alloc_ret(rettv);
ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP);
+ tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
tv_list_append_string(rettv->vval.v_list,
(const char *)xpc.xp_files[i], -1);
@@ -8268,7 +8268,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "menu_get(path [, modes])" function
static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
int modes = MENU_ALL_MODES;
if (argvars[1].v_type == VAR_STRING) {
const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]);
@@ -8429,7 +8429,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
}
if (count < 0) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenUnknown);
}
if (*fname != NUL && !error) {
@@ -9110,7 +9110,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} else if (strcmp(what, "args") == 0) {
rettv->v_type = VAR_LIST;
- if (tv_list_alloc_ret(rettv) != NULL) {
+ if (tv_list_alloc_ret(rettv, pt->pt_argc) != NULL) {
for (int i = 0; i < pt->pt_argc; i++) {
tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]);
}
@@ -9134,8 +9134,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// Returns information about signs placed in a buffer as list of dicts.
-static void get_buffer_signs(buf_T *buf, list_T *l)
+static list_T *get_buffer_signs(buf_T *buf)
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
+ list_T *const l = tv_list_alloc(kListLenMayKnow);
for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) {
dict_T *const d = tv_dict_alloc();
@@ -9146,6 +9148,7 @@ static void get_buffer_signs(buf_T *buf, list_T *l)
tv_list_append_dict(l, d);
}
+ return l;
}
/// Returns buffer options, variables and other attributes in a dictionary.
@@ -9169,7 +9172,7 @@ static dict_T *get_buffer_info(buf_T *buf)
tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars);
// List of windows displaying this buffer
- list_T *const windows = tv_list_alloc();
+ list_T *const windows = tv_list_alloc(kListLenMayKnow);
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer == buf) {
tv_list_append_number(windows, (varnumber_T)wp->handle);
@@ -9179,9 +9182,7 @@ static dict_T *get_buffer_info(buf_T *buf)
if (buf->b_signlist != NULL) {
// List of signs placed in this buffer
- list_T *const signs = tv_list_alloc();
- get_buffer_signs(buf, signs);
- tv_dict_add_list(dict, S_LEN("signs"), signs);
+ tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf));
}
return dict;
@@ -9195,7 +9196,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool sel_buflisted = false;
bool sel_bufloaded = false;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
// List of all the buffers or selected buffers
if (argvars[0].v_type == VAR_DICT) {
@@ -9254,35 +9255,33 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv)
{
- char_u *p;
-
- rettv->v_type = VAR_STRING;
+ rettv->v_type = (retlist ? VAR_LIST : VAR_STRING);
rettv->vval.v_string = NULL;
- if (retlist) {
- tv_list_alloc_ret(rettv);
- }
- if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0)
+ if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) {
+ if (retlist) {
+ tv_list_alloc_ret(rettv, 0);
+ }
return;
+ }
- if (!retlist) {
- if (start >= 1 && start <= buf->b_ml.ml_line_count)
- p = ml_get_buf(buf, start, FALSE);
- else
- p = (char_u *)"";
- rettv->vval.v_string = vim_strsave(p);
- } else {
- if (end < start)
- return;
-
- if (start < 1)
+ if (retlist) {
+ if (start < 1) {
start = 1;
- if (end > buf->b_ml.ml_line_count)
+ }
+ if (end > buf->b_ml.ml_line_count) {
end = buf->b_ml.ml_line_count;
+ }
+ tv_list_alloc_ret(rettv, end - start + 1);
while (start <= end) {
tv_list_append_string(rettv->vval.v_list,
(const char *)ml_get_buf(buf, start++, false), -1);
}
+ } else {
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count)
+ ? vim_strsave(ml_get_buf(buf, start, false))
+ : NULL);
}
}
@@ -9607,8 +9606,8 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
theend:
pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
- tv_list_alloc_ret(rettv);
ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
+ tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
@@ -9902,7 +9901,7 @@ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const linenr_T lnum = tv_get_lnum(argvars);
if (argvars[1].v_type == VAR_UNKNOWN) {
- end = 0;
+ end = lnum;
retlist = false;
} else {
end = tv_get_lnum(&argvars[1]);
@@ -9916,7 +9915,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg,
typval_T *rettv)
{
if (what_arg->v_type == VAR_UNKNOWN) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
if (is_qf || wp != NULL) {
(void)get_errorlist(wp, -1, rettv->vval.v_list);
}
@@ -9951,7 +9950,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
matchitem_T *cur = curwin->w_match_head;
int i;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
while (cur != NULL) {
dict_T *dict = tv_dict_alloc();
if (cur->match.regprog == NULL) {
@@ -9964,7 +9963,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (llpos->lnum == 0) {
break;
}
- list_T *l = tv_list_alloc();
+ list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0));
tv_list_append_number(l, (varnumber_T)llpos->lnum);
if (llpos->col > 0) {
tv_list_append_number(l, (varnumber_T)llpos->col);
@@ -10013,7 +10012,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos)
fp = var2fpos(&argvars[0], true, &fnum);
}
- list_T *l = tv_list_alloc_ret(rettv);
+ list_T *const l = tv_list_alloc_ret(rettv, 4 + (!!getcurpos));
tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0);
tv_list_append_number(l, ((fp != NULL)
? (varnumber_T)fp->lnum
@@ -10086,10 +10085,10 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (return_list) {
rettv->v_type = VAR_LIST;
- rettv->vval.v_list =
+ rettv->vval.v_list =
get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList);
if (rettv->vval.v_list == NULL) {
- rettv->vval.v_list = tv_list_alloc();
+ rettv->vval.v_list = tv_list_alloc(0);
}
tv_list_ref(rettv->vval.v_list);
} else {
@@ -10139,7 +10138,7 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)
tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx);
- list_T *const l = tv_list_alloc();
+ list_T *const l = tv_list_alloc(kListLenMayKnow);
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
tv_list_append_number(l, (varnumber_T)wp->handle);
}
@@ -10156,7 +10155,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tabpage_T *tparg = NULL;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN
+ ? 1
+ : kListLenMayKnow));
if (argvars[0].v_type != VAR_UNKNOWN) {
// Information about one tab page
@@ -10255,7 +10256,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *wparg = NULL;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
if (argvars[0].v_type != VAR_UNKNOWN) {
wparg = win_id2wp(argvars);
@@ -10480,9 +10481,9 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = ExpandOne(
&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL);
} else {
- tv_list_alloc_ret(rettv);
ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options,
WILD_ALL_KEEP);
+ tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
-1);
@@ -10531,7 +10532,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (rettv->v_type == VAR_STRING) {
rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n");
} else {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, ga.ga_len);
for (int i = 0; i < ga.ga_len; i++) {
tv_list_append_string(rettv->vval.v_list,
((const char **)(ga.ga_data))[i], -1);
@@ -11112,6 +11113,7 @@ void get_user_input(const typval_T *const argvars,
char defstr_buf[NUMBUFLEN];
char cancelreturn_buf[NUMBUFLEN];
char xp_name_buf[NUMBUFLEN];
+ char def[1] = { 0 };
if (argvars[0].v_type == VAR_DICT) {
if (argvars[1].v_type != VAR_UNKNOWN) {
emsgf(_("E5050: {opts} must be the only argument"));
@@ -11126,7 +11128,6 @@ void get_user_input(const typval_T *const argvars,
if (defstr == NULL) {
return;
}
- char def[1] = { 0 };
cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"),
cancelreturn_buf, def);
if (cancelreturn == NULL) { // error
@@ -11431,7 +11432,7 @@ static void dict_list(typval_T *const tv, typval_T *const rettv,
return;
}
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict));
TV_DICT_ITER(tv->vval.v_dict, di, {
typval_T tv = { .v_lock = VAR_UNLOCKED };
@@ -11448,7 +11449,7 @@ static void dict_list(typval_T *const tv, typval_T *const rettv,
}
case kDictListItems: {
// items()
- list_T *const sub_l = tv_list_alloc();
+ list_T *const sub_l = tv_list_alloc(2);
tv.v_type = VAR_LIST;
tv.vval.v_list = sub_l;
tv_list_ref(sub_l);
@@ -11778,7 +11779,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- list_T *rv = tv_list_alloc();
+ list_T *const rv = tv_list_alloc(tv_list_len(args));
// restore the parent queue for any jobs still alive
for (i = 0; i < tv_list_len(args); i++) {
@@ -12206,12 +12207,12 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
switch (type) {
// matchlist(): return empty list when there are no matches.
case kSomeMatchList: {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
break;
}
// matchstrpos(): return ["", -1, -1, -1]
case kSomeMatchStrPos: {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, 4);
tv_list_append_string(rettv->vval.v_list, "", 0);
tv_list_append_number(rettv->vval.v_list, -1);
tv_list_append_number(rettv->vval.v_list, -1);
@@ -12518,10 +12519,12 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tv_list_alloc_ret(rettv);
-
const int id = tv_get_number(&argvars[0]);
+ tv_list_alloc_ret(rettv, (id >= 1 && id <= 3
+ ? 2
+ : 0));
+
if (id >= 1 && id <= 3) {
matchitem_T *const m = (matchitem_T *)get_match(curwin, id);
@@ -12707,8 +12710,8 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG2(_(e_listarg), "msgpackdump()");
return;
}
- list_T *ret_list = tv_list_alloc_ret(rettv);
- list_T *list = argvars[0].vval.v_list;
+ list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow);
+ list_T *const list = argvars[0].vval.v_list;
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
@@ -12732,8 +12735,8 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG2(_(e_listarg), "msgpackparse()");
return;
}
- list_T *ret_list = tv_list_alloc_ret(rettv);
- const list_T *list = argvars[0].vval.v_list;
+ list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow);
+ const list_T *const list = argvars[0].vval.v_list;
if (tv_list_len(list) == 0) {
return;
}
@@ -12988,7 +12991,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else if (stride > 0 ? end + 1 < start : end - 1 > start) {
emsgf(_("E727: Start past end"));
} else {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, (end - start) / stride);
for (i = start; stride > 0 ? i <= end : i >= end; i += stride) {
tv_list_append_number(rettv->vval.v_list, (varnumber_T)i);
}
@@ -13019,8 +13022,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- tv_list_alloc_ret(rettv);
- list_T *const l = rettv->vval.v_list;
+ list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown);
// Always open the file in binary mode, library functions have a mind of
// their own about CR-LF conversion.
@@ -13233,7 +13235,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u),
"type punning will produce incorrect results on this platform");
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, 2);
tv_list_append_number(rettv->vval.v_list, u.split.high);
tv_list_append_number(rettv->vval.v_list, u.split.low);
}
@@ -13326,7 +13328,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (li == NULL) { // Didn't find "item2" after "item".
emsgf(_(e_invrange));
} else {
- tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv), cnt);
+ tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt),
+ cnt);
}
}
}
@@ -13356,7 +13359,7 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
varnumber_T n = tv_get_number(&argvars[1]);
if (argvars[0].v_type == VAR_LIST) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, (n > 0) * n * tv_list_len(argvars[0].vval.v_list));
while (n-- > 0) {
tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL);
}
@@ -14126,7 +14129,7 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int lnum = 0;
int col = 0;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, 2);
if (searchpair_cmn(argvars, &match_pos) > 0) {
lnum = match_pos.lnum;
@@ -14294,18 +14297,14 @@ do_searchpair (
static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
pos_T match_pos;
- int lnum = 0;
- int col = 0;
- int n;
int flags = 0;
- tv_list_alloc_ret(rettv);
+ const int n = search_cmn(argvars, &match_pos, &flags);
- n = search_cmn(argvars, &match_pos, &flags);
- if (n > 0) {
- lnum = match_pos.lnum;
- col = match_pos.col;
- }
+ tv_list_alloc_ret(rettv, 2 + (!!(flags & SP_SUBPAT)));
+
+ const int lnum = (n > 0 ? match_pos.lnum : 0);
+ const int col = (n > 0 ? match_pos.col : 0);
tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum);
tv_list_append_number(rettv->vval.v_list, (varnumber_T)col);
@@ -14321,7 +14320,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char **addrs = server_address_list(&n);
// Copy addrs into a linked list.
- list_T *l = tv_list_alloc_ret(rettv);
+ list_T *const l = tv_list_alloc_ret(rettv, n);
for (size_t i = 0; i < n; i++) {
tv_list_append_allocated_string(l, addrs[i]);
}
@@ -14717,7 +14716,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
dictitem_T *const di = tv_dict_find(d, S_LEN("pattern"));
if (di == NULL) {
if (s == NULL) {
- s = tv_list_alloc();
+ s = tv_list_alloc(9);
}
// match from matchaddpos()
@@ -15533,13 +15532,12 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
hlf_T attr = HLF_COUNT;
size_t len = 0;
- tv_list_alloc_ret(rettv);
-
if (argvars[0].v_type == VAR_UNKNOWN) {
// Find the start and length of the badly spelled word.
len = spell_move_to(curwin, FORWARD, true, true, &attr);
if (len != 0) {
word = (char *)get_cursor_pos_ptr();
+ curwin->w_set_curswant = true;
}
} else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) {
const char *str = tv_get_string_chk(&argvars[0]);
@@ -15559,6 +15557,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
assert(len <= INT_MAX);
+ tv_list_alloc_ret(rettv, 2);
tv_list_append_string(rettv->vval.v_list, word, len);
tv_list_append_string(rettv->vval.v_list,
(attr == HLF_SPB ? "bad"
@@ -15575,35 +15574,36 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
bool typeerr = false;
int maxcount;
- garray_T ga;
+ garray_T ga = GA_EMPTY_INIT_VALUE;
bool need_capital = false;
- tv_list_alloc_ret(rettv);
-
if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) {
const char *const str = tv_get_string(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN) {
maxcount = tv_get_number_chk(&argvars[1], &typeerr);
if (maxcount <= 0) {
- return;
+ goto f_spellsuggest_return;
}
if (argvars[2].v_type != VAR_UNKNOWN) {
need_capital = tv_get_number_chk(&argvars[2], &typeerr);
if (typeerr) {
- return;
+ goto f_spellsuggest_return;
}
}
- } else
+ } else {
maxcount = 25;
+ }
spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false);
+ }
- for (int i = 0; i < ga.ga_len; i++) {
- char *p = ((char **)ga.ga_data)[i];
- tv_list_append_allocated_string(rettv->vval.v_list, p);
- }
- ga_clear(&ga);
+f_spellsuggest_return:
+ tv_list_alloc_ret(rettv, (ptrdiff_t)ga.ga_len);
+ for (int i = 0; i < ga.ga_len; i++) {
+ char *const p = ((char **)ga.ga_data)[i];
+ tv_list_append_allocated_string(rettv->vval.v_list, p);
}
+ ga_clear(&ga);
}
static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
@@ -15635,10 +15635,11 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
pat = "[\\x01- ]\\+";
}
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
- if (typeerr)
+ if (typeerr) {
return;
+ }
regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
@@ -16265,7 +16266,6 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
memset(str, NUL, sizeof(str));
- tv_list_alloc_ret(rettv);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0
&& (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) {
(void)syn_get_id(curwin, lnum, col, false, NULL, false);
@@ -16278,14 +16278,12 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
cchar = lcs_conceal;
}
if (cchar != NUL) {
- if (has_mbyte)
- (*mb_char2bytes)(cchar, str);
- else
- str[0] = cchar;
+ utf_char2bytes(cchar, str);
}
}
}
+ tv_list_alloc_ret(rettv, 3);
tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0);
// -1 to auto-determine strlen
tv_list_append_string(rettv->vval.v_list, (const char *)str, -1);
@@ -16308,7 +16306,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& lnum <= curbuf->b_ml.ml_line_count
&& col >= 0
&& (size_t)col <= STRLEN(ml_get(lnum))) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
(void)syn_get_id(curwin, lnum, col, false, NULL, true);
int id;
@@ -16324,7 +16322,7 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty)
if (!keepempty && str[len - 1] == NL) {
len--;
}
- list_T *const list = tv_list_alloc();
+ list_T *const list = tv_list_alloc(kListLenMayKnow);
encode_list_write(list, str, len);
return list;
}
@@ -16370,7 +16368,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
if (res == NULL) {
if (retlist) {
// return an empty list when there's no output
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, 0);
} else {
rettv->vval.v_string = (char_u *) xstrdup("");
}
@@ -16436,7 +16434,7 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
if (wp != NULL) {
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
while (wp != NULL) {
tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum);
wp = wp->w_next;
@@ -16534,7 +16532,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char *fname;
tagname_T tn;
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenUnknown);
fname = xmalloc(MAXPATHL);
bool first = true;
@@ -16563,8 +16561,8 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[1].v_type != VAR_UNKNOWN) {
fname = tv_get_string(&argvars[1]);
}
- (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern,
- (char_u *)fname);
+ (void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown),
+ (char_u *)tag_pattern, (char_u *)fname);
}
/*
@@ -16670,6 +16668,18 @@ static void f_test_garbagecollect_now(typval_T *argvars,
garbage_collect(true);
}
+// "test_write_list_log()" function
+static void f_test_write_list_log(typval_T *const argvars,
+ typval_T *const rettv,
+ FunPtr fptr)
+{
+ const char *const fname = tv_get_string_chk(&argvars[0]);
+ if (fname == NULL) {
+ return;
+ }
+ list_write_log(fname);
+}
+
bool callback_from_typval(Callback *const callback, typval_T *const arg)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -16805,7 +16815,9 @@ static void add_timer_info_all(typval_T *rettv)
/// "timer_info([timer])" function
static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, (argvars[0].v_type != VAR_UNKNOWN
+ ? 1
+ : timers->table->n_occupied));
if (argvars[0].v_type != VAR_UNKNOWN) {
if (argvars[0].v_type != VAR_NUMBER) {
EMSG(_(e_number_exp));
@@ -17165,7 +17177,6 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_alloc_ret(rettv);
dict_T *dict = rettv->vval.v_dict;
- list_T *list;
tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced);
tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last);
@@ -17175,9 +17186,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur);
tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur);
- list = tv_list_alloc();
- u_eval_tree(curbuf->b_u_oldhead, list);
- tv_dict_add_list(dict, S_LEN("entries"), list);
+ tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead));
}
/*
@@ -17236,7 +17245,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "win_findbuf()" function
static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tv_list_alloc_ret(rettv);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
win_findbuf(argvars, rettv->vval.v_list);
}
@@ -17255,8 +17264,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "win_id2tabwin()" function
static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tv_list_alloc_ret(rettv);
- win_id2tabwin(argvars, rettv->vval.v_list);
+ win_id2tabwin(argvars, rettv);
}
/// "win_id2win()" function
@@ -17477,24 +17485,24 @@ write_list_error:
/// Saves a typval_T as a string.
///
-/// For lists, replaces NLs with NUL and separates items with NLs.
+/// For lists or buffers, replaces NLs with NUL and separates items with NLs.
///
-/// @param[in] tv A value to store as a string.
-/// @param[out] len The length of the resulting string or -1 on error.
+/// @param[in] tv Value to store as a string.
+/// @param[out] len Length of the resulting string or -1 on error.
/// @param[in] endnl If true, the output will end in a newline (if a list).
/// @returns an allocated string if `tv` represents a VimL string, list, or
/// number; NULL otherwise.
static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
+ *len = 0;
if (tv->v_type == VAR_UNKNOWN) {
- *len = 0;
return NULL;
}
- // For types other than list, let tv_get_string_buf_chk() get the value or
+ // For other types, let tv_get_string_buf_chk() get the value or
// print an error.
- if (tv->v_type != VAR_LIST) {
+ if (tv->v_type != VAR_LIST && tv->v_type != VAR_NUMBER) {
const char *ret = tv_get_string_chk(tv);
if (ret && (*len = strlen(ret))) {
return xmemdupz(ret, (size_t)(*len));
@@ -17504,8 +17512,40 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
}
}
+ if (tv->v_type == VAR_NUMBER) { // Treat number as a buffer-id.
+ buf_T *buf = buflist_findnr(tv->vval.v_number);
+ if (buf) {
+ for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+ for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) {
+ *len += 1;
+ }
+ *len += 1;
+ }
+ } else {
+ EMSGN(_(e_nobufnr), tv->vval.v_number);
+ *len = -1;
+ return NULL;
+ }
+
+ if (*len == 0) {
+ return NULL;
+ }
+
+ char *ret = xmalloc(*len + 1);
+ char *end = ret;
+ for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+ for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) {
+ *end++ = (*p == '\n') ? NUL : *p;
+ }
+ *end++ = '\n';
+ }
+ *end = NUL;
+ *len = end - ret;
+ return ret;
+ }
+
+ assert(tv->v_type == VAR_LIST);
// Pre-calculate the resulting length.
- *len = 0;
list_T *list = tv->vval.v_list;
TV_LIST_ITER_CONST(list, li, {
*len += strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1;
@@ -22369,7 +22409,7 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
return;
}
- list_T *args = tv_list_alloc();
+ list_T *args = tv_list_alloc(1);
tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1);
*rettv = eval_call_provider(name, "eval", args);
}
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 0c0a6881f6..b798eae187 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -86,7 +86,6 @@ typedef enum {
VV_OLDFILES,
VV_WINDOWID,
VV_PROGPATH,
- VV_COMMAND_OUTPUT,
VV_COMPLETED_ITEM,
VV_OPTION_NEW,
VV_OPTION_OLD,
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 54cbc54d78..daa3b637a3 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -313,6 +313,7 @@ return {
tempname={},
termopen={args={1, 2}},
test_garbagecollect_now={},
+ test_write_list_log={args=1},
timer_info={args={0,1}},
timer_pause={args=2},
timer_start={args={2,3}},
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index af4e055d23..17799b500c 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -150,7 +150,7 @@ static inline int json_decoder_pop(ValuesStackItem obj,
}
obj_di->di_tv = obj.val;
} else {
- list_T *const kv_pair = tv_list_alloc();
+ list_T *const kv_pair = tv_list_alloc(2);
tv_list_append_list(last_container.special_val, kv_pair);
tv_list_append_owned_tv(kv_pair, key.val);
tv_list_append_owned_tv(kv_pair, obj.val);
@@ -221,13 +221,18 @@ static inline int json_decoder_pop(ValuesStackItem obj,
/// Create a new special dictionary that ought to represent a MAP
///
/// @param[out] ret_tv Address where new special dictionary is saved.
+/// @param[in] len Expected number of items to be populated before list
+/// becomes accessible from VimL. It is still valid to
+/// underpopulate a list, value only controls how many elements
+/// will be allocated in advance. @see ListLenSpecials.
///
/// @return [allocated] list which should contain key-value pairs. Return value
/// may be safely ignored.
-list_T *decode_create_map_special_dict(typval_T *const ret_tv)
+list_T *decode_create_map_special_dict(typval_T *const ret_tv,
+ const ptrdiff_t len)
FUNC_ATTR_NONNULL_ALL
{
- list_T *const list = tv_list_alloc();
+ list_T *const list = tv_list_alloc(len);
tv_list_ref(list);
create_special_dict(ret_tv, kMPMap, ((typval_T) {
.v_type = VAR_LIST,
@@ -263,7 +268,7 @@ typval_T decode_string(const char *const s, const size_t len,
? ((s != NULL) && (memchr(s, NUL, len) != NULL))
: (bool)hasnul);
if (really_hasnul) {
- list_T *const list = tv_list_alloc();
+ list_T *const list = tv_list_alloc(kListLenMayKnow);
tv_list_ref(list);
typval_T tv;
create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) {
@@ -285,7 +290,7 @@ typval_T decode_string(const char *const s, const size_t len,
.v_type = VAR_STRING,
.v_lock = VAR_UNLOCKED,
.vval = { .v_string = (char_u *)(
- s_allocated ? (char *)s : xmemdupz(s, len)) },
+ (s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) },
};
}
}
@@ -843,7 +848,7 @@ json_decode_string_cycle_start:
break;
}
case '[': {
- list_T *list = tv_list_alloc();
+ list_T *list = tv_list_alloc(kListLenMayKnow);
tv_list_ref(list);
typval_T tv = {
.v_type = VAR_LIST,
@@ -864,7 +869,7 @@ json_decode_string_cycle_start:
list_T *val_list = NULL;
if (next_map_special) {
next_map_special = false;
- val_list = decode_create_map_special_dict(&tv);
+ val_list = decode_create_map_special_dict(&tv, kListLenMayKnow);
} else {
dict_T *dict = tv_dict_alloc();
dict->dv_refcount++;
@@ -964,7 +969,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
.vval = { .v_number = (varnumber_T) mobj.via.u64 },
};
} else {
- list_T *const list = tv_list_alloc();
+ list_T *const list = tv_list_alloc(4);
tv_list_ref(list);
create_special_dict(rettv, kMPInteger, ((typval_T) {
.v_type = VAR_LIST,
@@ -987,7 +992,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
.vval = { .v_number = (varnumber_T) mobj.via.i64 },
};
} else {
- list_T *const list = tv_list_alloc();
+ list_T *const list = tv_list_alloc(4);
tv_list_ref(list);
create_special_dict(rettv, kMPInteger, ((typval_T) {
.v_type = VAR_LIST,
@@ -1033,7 +1038,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
break;
}
case MSGPACK_OBJECT_ARRAY: {
- list_T *const list = tv_list_alloc();
+ list_T *const list = tv_list_alloc((ptrdiff_t)mobj.via.array.size);
tv_list_ref(list);
*rettv = (typval_T) {
.v_type = VAR_LIST,
@@ -1085,9 +1090,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
}
break;
msgpack_to_vim_generic_map: {}
- list_T *const list = decode_create_map_special_dict(rettv);
+ list_T *const list = decode_create_map_special_dict(
+ rettv, (ptrdiff_t)mobj.via.map.size);
for (size_t i = 0; i < mobj.via.map.size; i++) {
- list_T *const kv_pair = tv_list_alloc();
+ list_T *const kv_pair = tv_list_alloc(2);
tv_list_append_list(list, kv_pair);
typval_T key_tv = { .v_type = VAR_UNKNOWN };
@@ -1107,10 +1113,10 @@ msgpack_to_vim_generic_map: {}
break;
}
case MSGPACK_OBJECT_EXT: {
- list_T *const list = tv_list_alloc();
+ list_T *const list = tv_list_alloc(2);
tv_list_ref(list);
tv_list_append_number(list, mobj.via.ext.type);
- list_T *const ext_val_list = tv_list_alloc();
+ list_T *const ext_val_list = tv_list_alloc(kListLenMayKnow);
tv_list_append_list(list, ext_val_list);
create_special_dict(rettv, kMPExt, ((typval_T) {
.v_type = VAR_LIST,
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index ac6c8c8aa6..c8b550f902 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -31,6 +31,7 @@
#include "nvim/message.h"
// TODO(ZyX-I): Move line_breakcheck out of misc1
#include "nvim/misc1.h" // For line_breakcheck
+#include "nvim/os/fileio.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/typval.c.generated.h"
@@ -45,6 +46,70 @@ bool tv_in_free_unref_items = false;
const char *const tv_empty_string = "";
//{{{1 Lists
+//{{{2 List log
+#ifdef LOG_LIST_ACTIONS
+ListLog *list_log_first = NULL;
+ListLog *list_log_last = NULL;
+
+/// Write list log to the given file
+///
+/// @param[in] fname File to write log to. Will be appended to if already
+/// present.
+void list_write_log(const char *const fname)
+ FUNC_ATTR_NONNULL_ALL
+{
+ FileDescriptor fp;
+ const int fo_ret = file_open(&fp, fname, kFileCreate|kFileAppend, 0600);
+ if (fo_ret != 0) {
+ emsgf(_("E5142: Failed to open file %s: %s"), fname, os_strerror(fo_ret));
+ return;
+ }
+ for (ListLog *chunk = list_log_first; chunk != NULL;) {
+ for (size_t i = 0; i < chunk->size; i++) {
+ char buf[10 + 1 + ((16 + 3) * 3) + (8 + 2) + 2];
+ // act : hex " c:" len "[]" "\n\0"
+ const ListLogEntry entry = chunk->entries[i];
+ const size_t snp_len = (size_t)snprintf(
+ buf, sizeof(buf),
+ "%-10.10s: l:%016" PRIxPTR "[%08d] 1:%016" PRIxPTR " 2:%016" PRIxPTR
+ "\n",
+ entry.action, entry.l, entry.len, entry.li1, entry.li2);
+ assert(snp_len + 1 == sizeof(buf));
+ const ptrdiff_t fw_ret = file_write(&fp, buf, snp_len);
+ if (fw_ret != (ptrdiff_t)snp_len) {
+ assert(fw_ret < 0);
+ if (i) {
+ memmove(chunk->entries, chunk->entries + i,
+ sizeof(chunk->entries[0]) * (chunk->size - i));
+ chunk->size -= i;
+ }
+ emsgf(_("E5143: Failed to write to file %s: %s"),
+ fname, os_strerror((int)fw_ret));
+ return;
+ }
+ }
+ list_log_first = chunk->next;
+ xfree(chunk);
+ chunk = list_log_first;
+ }
+ const int fc_ret = file_close(&fp, true);
+ if (fc_ret != 0) {
+ emsgf(_("E5144: Failed to close file %s: %s"), fname, os_strerror(fc_ret));
+ }
+}
+
+#ifdef EXITFREE
+/// Free list log
+void list_free_log(void)
+{
+ for (ListLog *chunk = list_log_first; chunk != NULL;) {
+ list_log_first = chunk->next;
+ xfree(chunk);
+ chunk = list_log_first;
+ }
+}
+#endif
+#endif
//{{{2 List item
/// Allocate a list item
@@ -132,8 +197,14 @@ void tv_list_watch_fix(list_T *const l, const listitem_T *const item)
///
/// Caller should take care of the reference count.
///
+/// @param[in] len Expected number of items to be populated before list
+/// becomes accessible from VimL. It is still valid to
+/// underpopulate a list, value only controls how many elements
+/// will be allocated in advance. Currently does nothing.
+/// @see ListLenSpecials.
+///
/// @return [allocated] new list.
-list_T *tv_list_alloc(void)
+list_T *tv_list_alloc(const ptrdiff_t len)
FUNC_ATTR_NONNULL_RET
{
list_T *const list = xcalloc(1, sizeof(list_T));
@@ -145,6 +216,7 @@ list_T *tv_list_alloc(void)
list->lv_used_prev = NULL;
list->lv_used_next = gc_first_list;
gc_first_list = list;
+ list_log(list, NULL, (void *)(uintptr_t)len, "alloc");
return list;
}
@@ -174,6 +246,8 @@ void tv_list_init_static10(staticList10_T *const sl)
li->li_prev = li - 1;
li->li_next = li + 1;
}
+ list_log((const list_T *)sl, &sl->sl_items[0], &sl->sl_items[SL_SIZE - 1],
+ "s10init");
#undef SL_SIZE
}
@@ -185,6 +259,7 @@ void tv_list_init_static(list_T *const l)
{
memset(l, 0, sizeof(*l));
l->lv_refcount = DO_NOT_FREE_CNT;
+ list_log(l, NULL, NULL, "sinit");
}
/// Free items contained in a list
@@ -193,6 +268,7 @@ void tv_list_init_static(list_T *const l)
void tv_list_free_contents(list_T *const l)
FUNC_ATTR_NONNULL_ALL
{
+ list_log(l, NULL, NULL, "freecont");
for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) {
// Remove the item before deleting it.
l->lv_first = item->li_next;
@@ -222,6 +298,7 @@ void tv_list_free_list(list_T *const l)
if (l->lv_used_next != NULL) {
l->lv_used_next->lv_used_prev = l->lv_used_prev;
}
+ list_log(l, NULL, NULL, "freelist");
xfree(l);
}
@@ -266,6 +343,7 @@ void tv_list_drop_items(list_T *const l, listitem_T *const item,
listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
+ list_log(l, item, item2, "drop");
// Notify watchers.
for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) {
l->lv_len--;
@@ -283,6 +361,7 @@ void tv_list_drop_items(list_T *const l, listitem_T *const item,
item->li_prev->li_next = item2->li_next;
}
l->lv_idx_item = NULL;
+ list_log(l, l->lv_first, l->lv_last, "afterdrop");
}
/// Like tv_list_drop_items, but also frees all removed items
@@ -290,6 +369,7 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item,
listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
+ list_log(l, item, item2, "remove");
tv_list_drop_items(l, item, item2);
for (listitem_T *li = item;;) {
tv_clear(TV_LIST_ITEM_TV(li));
@@ -314,6 +394,7 @@ void tv_list_move_items(list_T *const l, listitem_T *const item,
const int cnt)
FUNC_ATTR_NONNULL_ALL
{
+ list_log(l, item, item2, "move");
tv_list_drop_items(l, item, item2);
item->li_prev = tgt_l->lv_last;
item2->li_next = NULL;
@@ -324,6 +405,7 @@ void tv_list_move_items(list_T *const l, listitem_T *const item,
}
tgt_l->lv_last = item2;
tgt_l->lv_len += cnt;
+ list_log(tgt_l, tgt_l->lv_first, tgt_l->lv_last, "movetgt");
}
/// Insert list item
@@ -352,6 +434,7 @@ void tv_list_insert(list_T *const l, listitem_T *const ni,
}
item->li_prev = ni;
l->lv_len++;
+ list_log(l, ni, item, "insert");
}
}
@@ -378,6 +461,7 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv,
void tv_list_append(list_T *const l, listitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
+ list_log(l, item, NULL, "append");
if (l->lv_last == NULL) {
// empty list
l->lv_first = item;
@@ -521,7 +605,7 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig,
return NULL;
}
- list_T *copy = tv_list_alloc();
+ list_T *copy = tv_list_alloc(tv_list_len(orig));
tv_list_ref(copy);
if (copyID != 0) {
// Do this before adding the items, because one of the items may
@@ -741,6 +825,7 @@ void tv_list_reverse(list_T *const l)
if (tv_list_len(l) <= 1) {
return;
}
+ list_log(l, NULL, NULL, "reverse");
#define SWAP(a, b) \
do { \
tmp = a; \
@@ -779,6 +864,7 @@ void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs,
if (len <= 1) {
return;
}
+ list_log(l, NULL, NULL, "sort");
int i = 0;
TV_LIST_ITER(l, li, {
ptrs[i].item = li;
@@ -867,6 +953,7 @@ listitem_T *tv_list_find(list_T *const l, int n)
// Cache the used index.
l->lv_idx = idx;
l->lv_idx_item = item;
+ list_log(l, l->lv_idx_item, (void *)(uintptr_t)l->lv_idx, "find");
return item;
}
@@ -1817,12 +1904,16 @@ void tv_dict_set_keys_readonly(dict_T *const dict)
/// Also sets reference count.
///
/// @param[out] ret_tv Structure where list is saved.
+/// @param[in] len Expected number of items to be populated before list
+/// becomes accessible from VimL. It is still valid to
+/// underpopulate a list, value only controls how many elements
+/// will be allocated in advance. @see ListLenSpecials.
///
/// @return [allocated] pointer to the created list.
-list_T *tv_list_alloc_ret(typval_T *const ret_tv)
+list_T *tv_list_alloc_ret(typval_T *const ret_tv, const ptrdiff_t len)
FUNC_ATTR_NONNULL_ALL
{
- list_T *const l = tv_list_alloc();
+ list_T *const l = tv_list_alloc(len);
ret_tv->vval.v_list = l;
ret_tv->v_type = VAR_LIST;
ret_tv->v_lock = VAR_UNLOCKED;
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index c9a9a3e7e8..40a1738d9e 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -20,6 +20,9 @@
#include "nvim/gettext.h"
#include "nvim/message.h"
#include "nvim/macros.h"
+#ifdef LOG_LIST_ACTIONS
+# include "nvim/memory.h"
+#endif
/// Type used for VimL VAR_NUMBER values
typedef int64_t varnumber_T;
@@ -31,6 +34,25 @@ typedef double float_T;
/// Refcount for dict or list that should not be freed
enum { DO_NOT_FREE_CNT = (INT_MAX / 2) };
+/// Additional values for tv_list_alloc() len argument
+enum {
+ /// List length is not known in advance
+ ///
+ /// To be used when there is neither a way to know how many elements will be
+ /// needed nor are any educated guesses.
+ kListLenUnknown = -1,
+ /// List length *should* be known, but is actually not
+ ///
+ /// All occurrences of this value should be eventually removed. This is for
+ /// the case when the only reason why list length is not known is that it
+ /// would be hard to code without refactoring, but refactoring is needed.
+ kListLenShouldKnow = -2,
+ /// List length may be known in advance, but it requires too much effort
+ ///
+ /// To be used when it looks impractical to determine list length.
+ kListLenMayKnow = -3,
+} ListLenSpecials;
+
/// Maximal possible value of varnumber_T variable
#define VARNUMBER_MAX INT64_MAX
#define UVARNUMBER_MAX UINT64_MAX
@@ -304,6 +326,96 @@ typedef struct {
typedef int (*ListSorter)(const void *, const void *);
+#ifdef LOG_LIST_ACTIONS
+
+/// List actions log entry
+typedef struct {
+ uintptr_t l; ///< List log entry belongs to.
+ uintptr_t li1; ///< First list item log entry belongs to, if applicable.
+ uintptr_t li2; ///< Second list item log entry belongs to, if applicable.
+ int len; ///< List length when log entry was created.
+ const char *action; ///< Logged action.
+} ListLogEntry;
+
+typedef struct list_log ListLog;
+
+/// List actions log
+struct list_log {
+ ListLog *next; ///< Next chunk or NULL.
+ size_t capacity; ///< Number of entries in current chunk.
+ size_t size; ///< Current chunk size.
+ ListLogEntry entries[]; ///< Actual log entries.
+};
+
+extern ListLog *list_log_first; ///< First list log chunk, NULL if missing
+extern ListLog *list_log_last; ///< Last list log chunk
+
+static inline ListLog *list_log_alloc(const size_t size)
+ REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Allocate a new log chunk and update globals
+///
+/// @param[in] size Number of entries in a new chunk.
+///
+/// @return [allocated] Newly allocated chunk.
+static inline ListLog *list_log_new(const size_t size)
+{
+ ListLog *ret = xmalloc(offsetof(ListLog, entries)
+ + size * sizeof(ret->entries[0]));
+ ret->size = 0;
+ ret->capacity = size;
+ ret->next = NULL;
+ if (list_log_first == NULL) {
+ list_log_first = ret;
+ } else {
+ list_log_last->next = ret;
+ }
+ list_log_last = ret;
+ return ret;
+}
+
+static inline void list_log(const list_T *const l,
+ const listitem_T *const li1,
+ const listitem_T *const li2,
+ const char *const action)
+ REAL_FATTR_ALWAYS_INLINE;
+
+/// Add new entry to log
+///
+/// If last chunk was filled it uses twice as much memory to allocate the next
+/// chunk.
+///
+/// @param[in] l List to which entry belongs.
+/// @param[in] li1 List item 1.
+/// @param[in] li2 List item 2, often used for integers and not list items.
+/// @param[in] action Logged action.
+static inline void list_log(const list_T *const l,
+ const listitem_T *const li1,
+ const listitem_T *const li2,
+ const char *const action)
+{
+ ListLog *tgt;
+ if (list_log_first == NULL) {
+ tgt = list_log_new(128);
+ } else if (list_log_last->size == list_log_last->capacity) {
+ tgt = list_log_new(list_log_last->capacity * 2);
+ } else {
+ tgt = list_log_last;
+ }
+ tgt->entries[tgt->size++] = (ListLogEntry) {
+ .l = (uintptr_t)l,
+ .li1 = (uintptr_t)li1,
+ .li2 = (uintptr_t)li2,
+ .len = (l == NULL ? 0 : l->lv_len),
+ .action = action,
+ };
+}
+#else
+# define list_log(...)
+# define list_write_log(...)
+# define list_free_log()
+#endif
+
// In a hashtab item "hi_key" points to "di_key" in a dictitem.
// This avoids adding a pointer to the hashtab item.
@@ -377,6 +489,7 @@ static inline int tv_list_len(const list_T *const l)
/// @param[in] l List to check.
static inline int tv_list_len(const list_T *const l)
{
+ list_log(l, NULL, NULL, "len");
if (l == NULL) {
return 0;
}
@@ -460,8 +573,10 @@ static inline listitem_T *tv_list_first(const list_T *const l)
static inline listitem_T *tv_list_first(const list_T *const l)
{
if (l == NULL) {
+ list_log(l, NULL, NULL, "first");
return NULL;
}
+ list_log(l, l->lv_first, NULL, "first");
return l->lv_first;
}
@@ -476,8 +591,10 @@ static inline listitem_T *tv_list_last(const list_T *const l)
static inline listitem_T *tv_list_last(const list_T *const l)
{
if (l == NULL) {
+ list_log(l, NULL, NULL, "last");
return NULL;
}
+ list_log(l, l->lv_last, NULL, "last");
return l->lv_last;
}
@@ -545,6 +662,7 @@ extern bool tv_in_free_unref_items;
#define _TV_LIST_ITER_MOD(modifier, l, li, code) \
do { \
modifier list_T *const l_ = (l); \
+ list_log(l_, NULL, NULL, "iter" #modifier); \
if (l_ != NULL) { \
for (modifier listitem_T *li = l_->lv_first; \
li != NULL; li = li->li_next) { \
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index c5825963c0..06379f159c 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3541,6 +3541,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL);
curwin->w_cursor.col = regmatch.endpos[0].col - 1;
+ if (curwin->w_cursor.col < 0) {
+ curwin->w_cursor.col = 0;
+ }
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec);
if (subflags.do_number || curwin->w_p_nu) {
int numw = number_width(curwin) + 1;
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index ec4ce63e17..28b021d4e4 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -3758,7 +3758,7 @@ static void script_host_execute(char *name, exarg_T *eap)
char *const script = script_get(eap, &len);
if (script != NULL) {
- list_T *const args = tv_list_alloc();
+ list_T *const args = tv_list_alloc(3);
// script
tv_list_append_allocated_string(args, script);
// current range
@@ -3773,7 +3773,7 @@ static void script_host_execute_file(char *name, exarg_T *eap)
uint8_t buffer[MAXPATHL];
vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false);
- list_T *args = tv_list_alloc();
+ list_T *args = tv_list_alloc(3);
// filename
tv_list_append_string(args, (const char *)buffer, -1);
// current range
@@ -3784,7 +3784,7 @@ static void script_host_execute_file(char *name, exarg_T *eap)
static void script_host_do_range(char *name, exarg_T *eap)
{
- list_T *args = tv_list_alloc();
+ list_T *args = tv_list_alloc(3);
tv_list_append_number(args, (int)eap->line1);
tv_list_append_number(args, (int)eap->line2);
tv_list_append_string(args, (const char *)eap->arg, -1);
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index a5a8804e1c..82fac8d78e 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -2164,7 +2164,13 @@ getexmodeline (
/* Get one character at a time. Don't use inchar(), it can't handle
* special characters. */
prev_char = c1;
- c1 = vgetc();
+
+ // Check for a ":normal" command and no more characters left.
+ if (ex_normal_busy > 0 && typebuf.tb_len == 0) {
+ c1 = '\n';
+ } else {
+ c1 = vgetc();
+ }
/*
* Handle line editing.
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 1b5d3472ab..7df1bf8429 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -3366,6 +3366,10 @@ set_context_in_map_cmd (
arg = skipwhite(arg + 8);
continue;
}
+ if (STRNCMP(arg, "<special>", 9) == 0) {
+ arg = skipwhite(arg + 9);
+ continue;
+ }
if (STRNCMP(arg, "<script>", 8) == 0) {
arg = skipwhite(arg + 8);
continue;
@@ -3408,21 +3412,24 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
for (round = 1; round <= 2; ++round) {
count = 0;
- for (i = 0; i < 6; ++i) {
- if (i == 0)
+ for (i = 0; i < 7; i++) {
+ if (i == 0) {
p = (char_u *)"<silent>";
- else if (i == 1)
+ } else if (i == 1) {
p = (char_u *)"<unique>";
- else if (i == 2)
+ } else if (i == 2) {
p = (char_u *)"<script>";
- else if (i == 3)
+ } else if (i == 3) {
p = (char_u *)"<expr>";
- else if (i == 4 && !expand_buffer)
+ } else if (i == 4 && !expand_buffer) {
p = (char_u *)"<buffer>";
- else if (i == 5)
+ } else if (i == 5) {
p = (char_u *)"<nowait>";
- else
+ } else if (i == 6) {
+ p = (char_u *)"<special>";
+ } else {
continue;
+ }
if (vim_regexec(regmatch, p, (colnr_T)0)) {
if (round == 1)
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index 0eb5a41b1e..eb0653c432 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -481,7 +481,7 @@ char_u *get_special_key_name(int c, int modifiers)
return string;
}
-/// Try translating a <> name
+/// Try translating a <> name ("keycode").
///
/// @param[in,out] srcp Source from which <> are translated. Is advanced to
/// after the <> name if there is a match.
@@ -619,9 +619,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
// Modifier with single letter, or special key name.
if (in_string && last_dash[1] == '\\' && last_dash[2] == '"') {
- off = 2;
+ // Special case for a double-quoted string
+ off = l = 2;
+ } else {
+ l = mb_ptr2len(last_dash + 1);
}
- l = mb_ptr2len(last_dash + 1);
if (modifiers != 0 && last_dash[l + 1] == '>') {
key = PTR2CHAR(last_dash + off);
} else {
@@ -834,7 +836,7 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len,
}
slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen, true,
- true);
+ false);
if (slen) {
dlen += slen;
continue;
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index 61cb428923..9e3063b164 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -211,7 +211,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
size_t len;
const char *s = lua_tolstring(lstate, -2, &len);
if (cur.special) {
- list_T *const kv_pair = tv_list_alloc();
+ list_T *const kv_pair = tv_list_alloc(2);
typval_T s_tv = decode_string(s, len, kTrue, false, false);
if (s_tv.v_type == VAR_UNKNOWN) {
@@ -321,7 +321,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
switch (table_props.type) {
case kObjectTypeArray: {
cur.tv->v_type = VAR_LIST;
- cur.tv->vval.v_list = tv_list_alloc();
+ cur.tv->vval.v_list = tv_list_alloc((ptrdiff_t)table_props.maxidx);
tv_list_ref(cur.tv->vval.v_list);
if (table_props.maxidx != 0) {
cur.container = true;
@@ -338,7 +338,8 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
} else {
cur.special = table_props.has_string_with_nul;
if (table_props.has_string_with_nul) {
- decode_create_map_special_dict(cur.tv);
+ decode_create_map_special_dict(
+ cur.tv, (ptrdiff_t)table_props.string_keys_num);
assert(cur.tv->v_type == VAR_DICT);
dictitem_T *const val_di = tv_dict_find(cur.tv->vval.v_dict,
S_LEN("_VAL"));
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 0b24023ad0..015df5d070 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -412,7 +412,7 @@ int main(int argc, char **argv)
}
// It's better to make v:oldfiles an empty list than NULL.
if (get_vim_var_list(VV_OLDFILES) == NULL) {
- set_vim_var_list(VV_OLDFILES, tv_list_alloc());
+ set_vim_var_list(VV_OLDFILES, tv_list_alloc(0));
}
/*
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 328b96fd5c..a66ab6a3cc 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -559,6 +559,7 @@ void time_to_bytes(time_t time_, uint8_t buf[8])
#include "nvim/tag.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
+#include "nvim/eval/typval.h"
/*
* Free everything that we allocated.
@@ -692,6 +693,7 @@ void free_all_mem(void)
free_screenlines();
clear_hl_tables();
+ list_free_log();
}
#endif
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 01c8e94bac..42417f75d5 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -714,7 +714,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
}
} else {
// visit recursively all children
- list_T *children_list = tv_list_alloc();
+ list_T *const children_list = tv_list_alloc(kListLenMayKnow);
for (menu = menu->children; menu != NULL; menu = menu->next) {
dict_T *dic = menu_get_recursive(menu, modes);
if (tv_dict_len(dict) > 0) {
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index f7ee2950ef..a498c7f4da 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -749,8 +749,9 @@ open_line (
// Postpone calling changed_lines(), because it would mess up folding
// with markers.
// Skip mark_adjust when adding a line after the last one, there can't
- // be marks there.
- if (curwin->w_cursor.lnum + 1 < curbuf->b_ml.ml_line_count) {
+ // be marks there. But still needed in diff mode.
+ if (curwin->w_cursor.lnum + 1 < curbuf->b_ml.ml_line_count
+ || curwin->w_p_diff) {
mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, false);
}
did_append = true;
@@ -1864,8 +1865,8 @@ void appended_lines(linenr_T lnum, long count)
void appended_lines_mark(linenr_T lnum, long count)
{
// Skip mark_adjust when adding a line after the last one, there can't
- // be marks there.
- if (lnum + count < curbuf->b_ml.ml_line_count) {
+ // be marks there. But it's still needed in diff mode.
+ if (lnum + count < curbuf->b_ml.ml_line_count || curwin->w_p_diff) {
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, count, 0L, false);
}
changed_lines(lnum + 1, 0, lnum + 1, count);
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 1103fe15d2..ab20be7246 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -5687,6 +5687,8 @@ static void nv_brackets(cmdarg_T *cap)
cap->nchar == 's', false, NULL) == 0) {
clearopbeep(cap->oap);
break;
+ } else {
+ curwin->w_set_curswant = true;
}
if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped)
foldOpenCursor();
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index a5e131190d..3a338b1417 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -2564,7 +2564,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
dict_T *dict = get_vim_var_dict(VV_EVENT);
// the yanked text
- list_T *list = tv_list_alloc();
+ list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
}
@@ -3177,9 +3177,9 @@ error:
curbuf->b_op_start.lnum++;
}
// Skip mark_adjust when adding lines after the last one, there
- // can't be marks there.
+ // can't be marks there. But still needed in diff mode.
if (curbuf->b_op_start.lnum + (y_type == kMTCharWise) - 1 + nr_lines
- < curbuf->b_ml.ml_line_count) {
+ < curbuf->b_ml.ml_line_count || curwin->w_p_diff) {
mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise),
(linenr_T)MAXLNUM, nr_lines, 0L, false);
}
@@ -4854,7 +4854,7 @@ static void *get_reg_wrap_one_line(char_u *s, int flags)
if (!(flags & kGRegList)) {
return s;
}
- list_T *const list = tv_list_alloc();
+ list_T *const list = tv_list_alloc(1);
tv_list_append_allocated_string(list, (char *)s);
return list;
}
@@ -4904,7 +4904,7 @@ void *get_reg_contents(int regname, int flags)
return NULL;
if (flags & kGRegList) {
- list_T *list = tv_list_alloc();
+ list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
}
@@ -5593,7 +5593,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
}
free_register(reg);
- list_T *const args = tv_list_alloc();
+ list_T *const args = tv_list_alloc(1);
const char regname = (char)name;
tv_list_append_string(args, &regname, 1);
@@ -5712,15 +5712,13 @@ static void set_clipboard(int name, yankreg_T *reg)
return;
}
- list_T *lines = tv_list_alloc();
+ list_T *const lines = tv_list_alloc(
+ (ptrdiff_t)reg->y_size + (reg->y_type != kMTCharWise));
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(lines, (const char *)reg->y_array[i], -1);
}
- list_T *args = tv_list_alloc();
- tv_list_append_list(args, lines);
-
char regtype;
switch (reg->y_type) {
case kMTLineWise: {
@@ -5741,10 +5739,11 @@ static void set_clipboard(int name, yankreg_T *reg)
assert(false);
}
}
- tv_list_append_string(args, &regtype, 1);
- const char regname = (char)name;
- tv_list_append_string(args, &regname, 1);
+ list_T *args = tv_list_alloc(3);
+ tv_list_append_list(args, lines);
+ tv_list_append_string(args, &regtype, 1);
+ tv_list_append_string(args, ((char[]) { (char)name }), 1);
(void)eval_call_provider("clipboard", "set", args);
}
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index 5d68473982..d294f9139b 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -39,9 +39,9 @@
/// @param[in] fname File name to open.
/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and
/// writing to the file at once is not supported, so either
-/// FILE_WRITE_ONLY or FILE_READ_ONLY is required.
+/// kFileWriteOnly or kFileReadOnly is required.
/// @param[in] mode Permissions for the newly created file (ignored if flags
-/// does not have FILE_CREATE\*).
+/// does not have kFileCreate\*).
///
/// @return Error code (@see os_strerror()) or 0.
int file_open(FileDescriptor *const ret_fp, const char *const fname,
@@ -120,7 +120,7 @@ int file_open_fd(FileDescriptor *const ret_fp, const int fd, const bool wr)
/// @param[in] fname File name to open.
/// @param[in] flags Flags, @see FileOpenFlags.
/// @param[in] mode Permissions for the newly created file (ignored if flags
-/// does not have FILE_CREATE\*).
+/// does not have kFileCreate\*).
///
/// @return [allocated] Opened file or NULL in case of error.
FileDescriptor *file_open_new(int *const error, const char *const fname,
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 7d6f2abd7f..405500767d 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -188,7 +188,7 @@ size_t input_enqueue(String keys)
uint8_t buf[6] = { 0 };
unsigned int new_size
= trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, true,
- true);
+ false);
if (new_size) {
new_size = handle_mouse_event(&ptr, buf, new_size);
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 224e43008d..63252df3dc 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -4193,7 +4193,7 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
}
}
if ((status == OK) && (flags & QF_GETLIST_ITEMS)) {
- list_T *l = tv_list_alloc();
+ list_T *l = tv_list_alloc(kListLenMayKnow);
(void)get_errorlist(wp, qf_idx, l);
tv_dict_add_list(retdict, S_LEN("items"), l);
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index ddc3681867..e4de43b49e 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -7044,7 +7044,7 @@ list_T *reg_submatch_list(int no)
colnr_T scol = rsm.sm_mmatch->startpos[no].col;
colnr_T ecol = rsm.sm_mmatch->endpos[no].col;
- list = tv_list_alloc();
+ list = tv_list_alloc(elnum - slnum + 1);
s = (const char *)reg_getline_submatch(slnum) + scol;
if (slnum == elnum) {
@@ -7063,7 +7063,7 @@ list_T *reg_submatch_list(int no)
if (s == NULL || rsm.sm_match->endp[no] == NULL) {
return NULL;
}
- list = tv_list_alloc();
+ list = tv_list_alloc(1);
tv_list_append_string(list, s, (const char *)rsm.sm_match->endp[no] - s);
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index ed96e98d32..8a29734025 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -3143,14 +3143,15 @@ win_line (
}
--n_extra;
} else {
+ int c0;
+
if (p_extra_free != NULL) {
xfree(p_extra_free);
p_extra_free = NULL;
}
- /*
- * Get a character from the line itself.
- */
- c = *ptr;
+
+ // Get a character from the line itself.
+ c0 = c = *ptr;
if (has_mbyte) {
mb_c = c;
if (enc_utf8) {
@@ -3160,11 +3161,12 @@ win_line (
mb_utf8 = FALSE;
if (mb_l > 1) {
mb_c = utfc_ptr2char(ptr, u8cc);
- /* Overlong encoded ASCII or ASCII with composing char
- * is displayed normally, except a NUL. */
- if (mb_c < 0x80)
- c = mb_c;
- mb_utf8 = TRUE;
+ // Overlong encoded ASCII or ASCII with composing char
+ // is displayed normally, except a NUL.
+ if (mb_c < 0x80) {
+ c0 = c = mb_c;
+ }
+ mb_utf8 = true;
/* At start of the line we can have a composing char.
* Draw it as a space with a composing char. */
@@ -3428,10 +3430,8 @@ win_line (
char_attr = hl_combine_attr(char_attr, term_attrs[vcol]);
}
- /*
- * Found last space before word: check for line break.
- */
- if (wp->w_p_lbr && vim_isbreak(c) && !vim_isbreak(*ptr)) {
+ // Found last space before word: check for line break.
+ if (wp->w_p_lbr && c0 == c && vim_isbreak(c) && !vim_isbreak(*ptr)) {
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?
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 1eb1a25a19..0a266382ec 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -1382,13 +1382,13 @@ int searchc(cmdarg_T *cap, int t_cmd)
col -= (*mb_head_off)(p, p + col - 1) + 1;
}
if (lastc_bytelen == 1) {
- if (p[col] == c && stop)
+ if (p[col] == c && stop) {
break;
- } else {
- if (memcmp(p + col, lastc_bytes, lastc_bytelen) == 0 && stop)
+ }
+ } else if (STRNCMP(p + col, lastc_bytes, lastc_bytelen) == 0 && stop) {
break;
}
- stop = TRUE;
+ stop = true;
}
} else {
for (;; ) {
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index ce9303f14d..c00fd912ec 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1217,7 +1217,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
khash_t(fnamebufs) fname_bufs = KHASH_EMPTY_TABLE(fnamebufs);
khash_t(strset) oldfiles_set = KHASH_EMPTY_TABLE(strset);
if (get_old_files && (oldfiles_list == NULL || force)) {
- oldfiles_list = tv_list_alloc();
+ oldfiles_list = tv_list_alloc(kListLenUnknown);
set_vim_var_list(VV_OLDFILES, oldfiles_list);
}
ShaDaReadResult srni_ret;
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index d2b2575f6a..38392509a5 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -1484,21 +1484,23 @@ spell_move_to (
return found_len;
}
- if (curline)
+ if (curline) {
break; // only check cursor line
+ }
+
+ // If we are back at the starting line and searched it again there
+ // is no match, give up.
+ if (lnum == wp->w_cursor.lnum && wrapped) {
+ break;
+ }
// Advance to next line.
if (dir == BACKWARD) {
- // If we are back at the starting line and searched it again there
- // is no match, give up.
- if (lnum == wp->w_cursor.lnum && wrapped)
- break;
-
- if (lnum > 1)
- --lnum;
- else if (!p_ws)
+ if (lnum > 1) {
+ lnum--;
+ } else if (!p_ws) {
break; // at first line and 'nowrapscan'
- else {
+ } else {
// Wrap around to the end of the buffer. May search the
// starting line again and accept the last match.
lnum = wp->w_buffer->b_ml.ml_line_count;
@@ -1523,8 +1525,9 @@ spell_move_to (
// If we are back at the starting line and there is no match then
// give up.
- if (lnum == wp->w_cursor.lnum && (!found_one || wrapped))
+ if (lnum == wp->w_cursor.lnum && !found_one) {
break;
+ }
// Skip the characters at the start of the next line that were
// included in a match crossing line boundaries.
@@ -2565,7 +2568,7 @@ static bool spell_iswordp(char_u *p, win_T *wp)
int c;
if (has_mbyte) {
- l = MB_BYTE2LEN(*p);
+ l = MB_PTR2LEN(p);
s = p;
if (l == 1) {
// be quick for ASCII
@@ -3136,8 +3139,13 @@ spell_find_suggest (
if (su->su_badlen >= MAXWLEN)
su->su_badlen = MAXWLEN - 1; // just in case
STRLCPY(su->su_badword, su->su_badptr, su->su_badlen + 1);
- (void)spell_casefold(su->su_badptr, su->su_badlen,
- su->su_fbadword, MAXWLEN);
+ (void)spell_casefold(su->su_badptr, su->su_badlen, su->su_fbadword, MAXWLEN);
+
+ // TODO(vim): make this work if the case-folded text is longer than the
+ // original text. Currently an illegal byte causes wrong pointer
+ // computations.
+ su->su_fbadword[su->su_badlen] = NUL;
+
// get caps flags for bad word
su->su_badflags = badword_captype(su->su_badptr,
su->su_badptr + su->su_badlen);
@@ -4107,10 +4115,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
&& goodword_ends) {
int l;
- if (has_mbyte)
- l = MB_BYTE2LEN(fword[sp->ts_fidx]);
- else
- l = 1;
+ l = MB_PTR2LEN(fword + sp->ts_fidx);
if (fword_ends) {
// Copy the skipped character to preword.
memmove(preword + sp->ts_prewordlen,
@@ -4256,8 +4261,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Correct ts_fidx for the byte length of the
// character (we didn't check that before).
sp->ts_fidx = sp->ts_fcharstart
- + MB_BYTE2LEN(
- fword[sp->ts_fcharstart]);
+ + MB_PTR2LEN(fword + sp->ts_fcharstart);
// For changing a composing character adjust
// the score from SCORE_SUBST to
@@ -4363,11 +4367,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// results.
if (has_mbyte) {
c = mb_ptr2char(fword + sp->ts_fidx);
- stack[depth].ts_fidx += MB_BYTE2LEN(fword[sp->ts_fidx]);
- if (enc_utf8 && utf_iscomposing(c))
+ stack[depth].ts_fidx += MB_PTR2LEN(fword + sp->ts_fidx);
+ if (enc_utf8 && utf_iscomposing(c)) {
stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP;
- else if (c == mb_ptr2char(fword + stack[depth].ts_fidx))
+ } else if (c == mb_ptr2char(fword + stack[depth].ts_fidx)) {
stack[depth].ts_score -= SCORE_DEL - SCORE_DELDUP;
+ }
} else {
++stack[depth].ts_fidx;
if (fword[sp->ts_fidx] == fword[sp->ts_fidx + 1])
@@ -4549,9 +4554,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Undo the STATE_SWAP swap: "21" -> "12".
p = fword + sp->ts_fidx;
if (has_mbyte) {
- n = MB_BYTE2LEN(*p);
+ n = MB_PTR2LEN(p);
c = mb_ptr2char(p + n);
- memmove(p + MB_BYTE2LEN(p[n]), p, n);
+ memmove(p + MB_PTR2LEN(p + n), p, n);
mb_char2bytes(c, p);
} else {
c = *p;
@@ -4624,11 +4629,11 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Undo STATE_SWAP3: "321" -> "123"
p = fword + sp->ts_fidx;
if (has_mbyte) {
- n = MB_BYTE2LEN(*p);
+ n = MB_PTR2LEN(p);
c2 = mb_ptr2char(p + n);
- fl = MB_BYTE2LEN(p[n]);
+ fl = MB_PTR2LEN(p + n);
c = mb_ptr2char(p + n + fl);
- tl = MB_BYTE2LEN(p[n + fl]);
+ tl = MB_PTR2LEN(p + n + fl);
memmove(p + fl + tl, p, n);
mb_char2bytes(c, p);
mb_char2bytes(c2, p + tl);
@@ -4687,10 +4692,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Undo ROT3L: "231" -> "123"
p = fword + sp->ts_fidx;
if (has_mbyte) {
- n = MB_BYTE2LEN(*p);
- n += MB_BYTE2LEN(p[n]);
+ n = MB_PTR2LEN(p);
+ n += MB_PTR2LEN(p + n);
c = mb_ptr2char(p + n);
- tl = MB_BYTE2LEN(p[n]);
+ tl = MB_PTR2LEN(p + n);
memmove(p + tl, p, n);
mb_char2bytes(c, p);
} else {
@@ -4740,9 +4745,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
p = fword + sp->ts_fidx;
if (has_mbyte) {
c = mb_ptr2char(p);
- tl = MB_BYTE2LEN(*p);
- n = MB_BYTE2LEN(p[tl]);
- n += MB_BYTE2LEN(p[tl + n]);
+ tl = MB_PTR2LEN(p);
+ n = MB_PTR2LEN(p + tl);
+ n += MB_PTR2LEN(p + tl + n);
memmove(p, p + tl, n);
mb_char2bytes(c, p + n);
} else {
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 1f7f616782..df066e7ad3 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -3656,7 +3656,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
flags |= WF_REGION;
l = *p - '0';
- if (l > spin->si_region_count) {
+ if (l == 0 || l > spin->si_region_count) {
smsg(_("Invalid region nr in %s line %d: %s"),
fname, lnum, p);
break;
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 01d2728f3d..05bc6c9d96 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -3022,9 +3022,9 @@ static void syn_cmd_conceal(exarg_T *eap, int syncing)
next = skiptowhite(arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_conceal) {
- MSG(_("syn conceal on"));
+ MSG(_("syn conceal on"));
} else {
- MSG(_("syn conceal off"));
+ MSG(_("syn conceal off"));
}
} else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2) {
curwin->w_s->b_syn_conceal = true;
@@ -3050,9 +3050,9 @@ static void syn_cmd_case(exarg_T *eap, int syncing)
next = skiptowhite(arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_ic) {
- MSG(_("syntax case ignore"));
+ MSG(_("syntax case ignore"));
} else {
- MSG(_("syntax case match"));
+ MSG(_("syntax case match"));
}
} else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5) {
curwin->w_s->b_syn_ic = false;
@@ -3078,11 +3078,11 @@ static void syn_cmd_spell(exarg_T *eap, int syncing)
next = skiptowhite(arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_spell == SYNSPL_TOP) {
- MSG(_("syntax spell toplevel"));
+ MSG(_("syntax spell toplevel"));
} else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP) {
- MSG(_("syntax spell notoplevel"));
+ MSG(_("syntax spell notoplevel"));
} else {
- MSG(_("syntax spell default"));
+ MSG(_("syntax spell default"));
}
} else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) {
curwin->w_s->b_syn_spell = SYNSPL_TOP;
@@ -5287,7 +5287,7 @@ get_id_list (
id += current_syn_inc_tag;
} else if (name[1] == '@') {
if (skip) {
- id = -1;
+ id = -1;
} else {
id = syn_check_cluster(name + 2, (int)(end - p - 1));
}
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index be9d621c7d..f23465e501 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -667,7 +667,7 @@ do_tag (
fname = xmalloc(MAXPATHL + 1);
cmd = xmalloc(CMDBUFFSIZE + 1);
- list = tv_list_alloc();
+ list = tv_list_alloc(num_matches);
for (i = 0; i < num_matches; ++i) {
int len, cmd_len;
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 5af8dd20cd..c1e6eedf94 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -40,6 +40,7 @@ SCRIPTS ?= $(SCRIPTS_DEFAULT)
# Tests using runtest.vim.
# Keep test_alot*.res as the last one, sort the others.
NEW_TESTS ?= \
+ test_arabic.vim \
test_autocmd.res \
test_bufwintabinfo.res \
test_changedtick.res \
@@ -81,11 +82,13 @@ NEW_TESTS ?= \
test_search.res \
test_signs.res \
test_smartindent.res \
+ test_spell.res \
test_stat.res \
test_startup.res \
test_startup_utf8.res \
test_substitute.res \
test_syntax.res \
+ test_system.res \
test_tabpage.res \
test_textobjects.res \
test_timers.res \
diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim
index 72cfea96c6..d8e2d1d718 100644
--- a/src/nvim/testdir/shared.vim
+++ b/src/nvim/testdir/shared.vim
@@ -1,5 +1,21 @@
" Functions shared by several tests.
+" {Nvim}
+" Filepath captured from output may be truncated, like this:
+" /home/va...estdir/Xtest-tmpdir/nvimxbXN4i/10
+" Get last 2 segments, then combine with $TMPDIR.
+func! Fix_truncated_tmpfile(fname)
+ " sanity check
+ if $TMPDIR ==# ''
+ throw '$TMPDIR is empty'
+ endif
+ if a:fname !~# $TMPDIR
+ throw '$TMPDIR not in fname: '.a:fname
+ endif
+ let last2segments = matchstr(a:fname, '[\/][^\/]\+[\/][^\/]\+$')
+ return $TMPDIR.last2segments
+endfunc
+
" Get the name of the Python executable.
" Also keeps it in s:python.
func PythonProg()
diff --git a/src/nvim/testdir/test_arabic.vim b/src/nvim/testdir/test_arabic.vim
new file mode 100644
index 0000000000..9a16833a4c
--- /dev/null
+++ b/src/nvim/testdir/test_arabic.vim
@@ -0,0 +1,472 @@
+" Simplistic testing of Arabic mode.
+
+if !has('arabic') || !has('multi_byte')
+ finish
+endif
+
+source view_util.vim
+
+" Return list of Unicode characters at line lnum.
+" Combining characters are treated as a single item.
+func s:get_chars(lnum)
+ call cursor(a:lnum, 1)
+ let chars = []
+ let numchars = strchars(getline('.'), 1)
+ for i in range(1, numchars)
+ exe 'norm ' i . '|'
+ let c=execute('ascii')
+ let c=substitute(c, '\n\?<.\{-}Hex\s*', 'U+', 'g')
+ let c=substitute(c, ',\s*Octal\s*\d*', '', 'g')
+ call add(chars, c)
+ endfor
+ return chars
+endfunc
+
+func Test_arabic_toggle()
+ set arabic
+ call assert_equal(1, &rightleft)
+ call assert_equal(1, &arabicshape)
+ call assert_equal('arabic', &keymap)
+ call assert_equal(1, &delcombine)
+
+ set iminsert=1 imsearch=1
+ set arabic&
+ call assert_equal(0, &rightleft)
+ call assert_equal(1, &arabicshape)
+ call assert_equal('arabic', &keymap)
+ call assert_equal(1, &delcombine)
+ call assert_equal(0, &iminsert)
+ call assert_equal(-1, &imsearch)
+
+ set arabicshape& keymap= delcombine&
+endfunc
+
+func Test_arabic_input()
+ new
+ set arabic
+ " Typing sghl in Arabic insert mode should show the
+ " Arabic word 'Salaam' i.e. 'peace', spelled:
+ " SEEN, LAM, ALEF, MEEM.
+ " See: https://www.mediawiki.org/wiki/VisualEditor/Typing/Right-to-left
+ call feedkeys('isghl!', 'tx')
+ call assert_match("^ *!\uFEE1\uFEFC\uFEB3$", ScreenLines(1, &columns)[0])
+ call assert_equal([
+ \ 'U+0633',
+ \ 'U+0644 U+0627',
+ \ 'U+0645',
+ \ 'U+21'], s:get_chars(1))
+
+ " Without shaping, it should give individual Arabic letters.
+ set noarabicshape
+ call assert_match("^ *!\u0645\u0627\u0644\u0633$", ScreenLines(1, &columns)[0])
+ call assert_equal([
+ \ 'U+0633',
+ \ 'U+0644',
+ \ 'U+0627',
+ \ 'U+0645',
+ \ 'U+21'], s:get_chars(1))
+
+ set arabic& arabicshape&
+ bwipe!
+endfunc
+
+func Test_arabic_toggle_keymap()
+ new
+ set arabic
+ call feedkeys("i12\<C-^>12\<C-^>12", 'tx')
+ call assert_match("^ *٢١21٢١$", ScreenLines(1, &columns)[0])
+ call assert_equal('١٢12١٢', getline('.'))
+ set arabic&
+ bwipe!
+endfunc
+
+func Test_delcombine()
+ new
+ set arabic
+ call feedkeys("isghl\<BS>\<BS>", 'tx')
+ call assert_match("^ *\uFEDE\uFEB3$", ScreenLines(1, &columns)[0])
+ call assert_equal(['U+0633', 'U+0644'], s:get_chars(1))
+
+ " Now the same with 'nodelcombine'
+ set nodelcombine
+ %d
+ call feedkeys("isghl\<BS>\<BS>", 'tx')
+ call assert_match("^ *\uFEB1$", ScreenLines(1, &columns)[0])
+ call assert_equal(['U+0633'], s:get_chars(1))
+ set arabic&
+ bwipe!
+endfunc
+
+" Values from src/arabic.h (not all used yet)
+let s:a_COMMA = "\u060C"
+let s:a_SEMICOLON = "\u061B"
+let s:a_QUESTION = "\u061F"
+let s:a_HAMZA = "\u0621"
+let s:a_ALEF_MADDA = "\u0622"
+let s:a_ALEF_HAMZA_ABOVE = "\u0623"
+let s:a_WAW_HAMZA = "\u0624"
+let s:a_ALEF_HAMZA_BELOW = "\u0625"
+let s:a_YEH_HAMZA = "\u0626"
+let s:a_ALEF = "\u0627"
+let s:a_BEH = "\u0628"
+let s:a_TEH_MARBUTA = "\u0629"
+let s:a_TEH = "\u062a"
+let s:a_THEH = "\u062b"
+let s:a_JEEM = "\u062c"
+let s:a_HAH = "\u062d"
+let s:a_KHAH = "\u062e"
+let s:a_DAL = "\u062f"
+let s:a_THAL = "\u0630"
+let s:a_REH = "\u0631"
+let s:a_ZAIN = "\u0632"
+let s:a_SEEN = "\u0633"
+let s:a_SHEEN = "\u0634"
+let s:a_SAD = "\u0635"
+let s:a_DAD = "\u0636"
+let s:a_TAH = "\u0637"
+let s:a_ZAH = "\u0638"
+let s:a_AIN = "\u0639"
+let s:a_GHAIN = "\u063a"
+let s:a_TATWEEL = "\u0640"
+let s:a_FEH = "\u0641"
+let s:a_QAF = "\u0642"
+let s:a_KAF = "\u0643"
+let s:a_LAM = "\u0644"
+let s:a_MEEM = "\u0645"
+let s:a_NOON = "\u0646"
+let s:a_HEH = "\u0647"
+let s:a_WAW = "\u0648"
+let s:a_ALEF_MAKSURA = "\u0649"
+let s:a_YEH = "\u064a"
+
+let s:a_FATHATAN = "\u064b"
+let s:a_DAMMATAN = "\u064c"
+let s:a_KASRATAN = "\u064d"
+let s:a_FATHA = "\u064e"
+let s:a_DAMMA = "\u064f"
+let s:a_KASRA = "\u0650"
+let s:a_SHADDA = "\u0651"
+let s:a_SUKUN = "\u0652"
+
+let s:a_MADDA_ABOVE = "\u0653"
+let s:a_HAMZA_ABOVE = "\u0654"
+let s:a_HAMZA_BELOW = "\u0655"
+
+let s:a_ZERO = "\u0660"
+let s:a_ONE = "\u0661"
+let s:a_TWO = "\u0662"
+let s:a_THREE = "\u0663"
+let s:a_FOUR = "\u0664"
+let s:a_FIVE = "\u0665"
+let s:a_SIX = "\u0666"
+let s:a_SEVEN = "\u0667"
+let s:a_EIGHT = "\u0668"
+let s:a_NINE = "\u0669"
+let s:a_PERCENT = "\u066a"
+let s:a_DECIMAL = "\u066b"
+let s:a_THOUSANDS = "\u066c"
+let s:a_STAR = "\u066d"
+let s:a_MINI_ALEF = "\u0670"
+
+let s:a_s_FATHATAN = "\ufe70"
+let s:a_m_TATWEEL_FATHATAN = "\ufe71"
+let s:a_s_DAMMATAN = "\ufe72"
+
+let s:a_s_KASRATAN = "\ufe74"
+
+let s:a_s_FATHA = "\ufe76"
+let s:a_m_FATHA = "\ufe77"
+let s:a_s_DAMMA = "\ufe78"
+let s:a_m_DAMMA = "\ufe79"
+let s:a_s_KASRA = "\ufe7a"
+let s:a_m_KASRA = "\ufe7b"
+let s:a_s_SHADDA = "\ufe7c"
+let s:a_m_SHADDA = "\ufe7d"
+let s:a_s_SUKUN = "\ufe7e"
+let s:a_m_SUKUN = "\ufe7f"
+
+let s:a_s_HAMZA = "\ufe80"
+let s:a_s_ALEF_MADDA = "\ufe81"
+let s:a_f_ALEF_MADDA = "\ufe82"
+let s:a_s_ALEF_HAMZA_ABOVE = "\ufe83"
+let s:a_f_ALEF_HAMZA_ABOVE = "\ufe84"
+let s:a_s_WAW_HAMZA = "\ufe85"
+let s:a_f_WAW_HAMZA = "\ufe86"
+let s:a_s_ALEF_HAMZA_BELOW = "\ufe87"
+let s:a_f_ALEF_HAMZA_BELOW = "\ufe88"
+let s:a_s_YEH_HAMZA = "\ufe89"
+let s:a_f_YEH_HAMZA = "\ufe8a"
+let s:a_i_YEH_HAMZA = "\ufe8b"
+let s:a_m_YEH_HAMZA = "\ufe8c"
+let s:a_s_ALEF = "\ufe8d"
+let s:a_f_ALEF = "\ufe8e"
+let s:a_s_BEH = "\ufe8f"
+let s:a_f_BEH = "\ufe90"
+let s:a_i_BEH = "\ufe91"
+let s:a_m_BEH = "\ufe92"
+let s:a_s_TEH_MARBUTA = "\ufe93"
+let s:a_f_TEH_MARBUTA = "\ufe94"
+let s:a_s_TEH = "\ufe95"
+let s:a_f_TEH = "\ufe96"
+let s:a_i_TEH = "\ufe97"
+let s:a_m_TEH = "\ufe98"
+let s:a_s_THEH = "\ufe99"
+let s:a_f_THEH = "\ufe9a"
+let s:a_i_THEH = "\ufe9b"
+let s:a_m_THEH = "\ufe9c"
+let s:a_s_JEEM = "\ufe9d"
+let s:a_f_JEEM = "\ufe9e"
+let s:a_i_JEEM = "\ufe9f"
+let s:a_m_JEEM = "\ufea0"
+let s:a_s_HAH = "\ufea1"
+let s:a_f_HAH = "\ufea2"
+let s:a_i_HAH = "\ufea3"
+let s:a_m_HAH = "\ufea4"
+let s:a_s_KHAH = "\ufea5"
+let s:a_f_KHAH = "\ufea6"
+let s:a_i_KHAH = "\ufea7"
+let s:a_m_KHAH = "\ufea8"
+let s:a_s_DAL = "\ufea9"
+let s:a_f_DAL = "\ufeaa"
+let s:a_s_THAL = "\ufeab"
+let s:a_f_THAL = "\ufeac"
+let s:a_s_REH = "\ufead"
+let s:a_f_REH = "\ufeae"
+let s:a_s_ZAIN = "\ufeaf"
+let s:a_f_ZAIN = "\ufeb0"
+let s:a_s_SEEN = "\ufeb1"
+let s:a_f_SEEN = "\ufeb2"
+let s:a_i_SEEN = "\ufeb3"
+let s:a_m_SEEN = "\ufeb4"
+let s:a_s_SHEEN = "\ufeb5"
+let s:a_f_SHEEN = "\ufeb6"
+let s:a_i_SHEEN = "\ufeb7"
+let s:a_m_SHEEN = "\ufeb8"
+let s:a_s_SAD = "\ufeb9"
+let s:a_f_SAD = "\ufeba"
+let s:a_i_SAD = "\ufebb"
+let s:a_m_SAD = "\ufebc"
+let s:a_s_DAD = "\ufebd"
+let s:a_f_DAD = "\ufebe"
+let s:a_i_DAD = "\ufebf"
+let s:a_m_DAD = "\ufec0"
+let s:a_s_TAH = "\ufec1"
+let s:a_f_TAH = "\ufec2"
+let s:a_i_TAH = "\ufec3"
+let s:a_m_TAH = "\ufec4"
+let s:a_s_ZAH = "\ufec5"
+let s:a_f_ZAH = "\ufec6"
+let s:a_i_ZAH = "\ufec7"
+let s:a_m_ZAH = "\ufec8"
+let s:a_s_AIN = "\ufec9"
+let s:a_f_AIN = "\ufeca"
+let s:a_i_AIN = "\ufecb"
+let s:a_m_AIN = "\ufecc"
+let s:a_s_GHAIN = "\ufecd"
+let s:a_f_GHAIN = "\ufece"
+let s:a_i_GHAIN = "\ufecf"
+let s:a_m_GHAIN = "\ufed0"
+let s:a_s_FEH = "\ufed1"
+let s:a_f_FEH = "\ufed2"
+let s:a_i_FEH = "\ufed3"
+let s:a_m_FEH = "\ufed4"
+let s:a_s_QAF = "\ufed5"
+let s:a_f_QAF = "\ufed6"
+let s:a_i_QAF = "\ufed7"
+let s:a_m_QAF = "\ufed8"
+let s:a_s_KAF = "\ufed9"
+let s:a_f_KAF = "\ufeda"
+let s:a_i_KAF = "\ufedb"
+let s:a_m_KAF = "\ufedc"
+let s:a_s_LAM = "\ufedd"
+let s:a_f_LAM = "\ufede"
+let s:a_i_LAM = "\ufedf"
+let s:a_m_LAM = "\ufee0"
+let s:a_s_MEEM = "\ufee1"
+let s:a_f_MEEM = "\ufee2"
+let s:a_i_MEEM = "\ufee3"
+let s:a_m_MEEM = "\ufee4"
+let s:a_s_NOON = "\ufee5"
+let s:a_f_NOON = "\ufee6"
+let s:a_i_NOON = "\ufee7"
+let s:a_m_NOON = "\ufee8"
+let s:a_s_HEH = "\ufee9"
+let s:a_f_HEH = "\ufeea"
+let s:a_i_HEH = "\ufeeb"
+let s:a_m_HEH = "\ufeec"
+let s:a_s_WAW = "\ufeed"
+let s:a_f_WAW = "\ufeee"
+let s:a_s_ALEF_MAKSURA = "\ufeef"
+let s:a_f_ALEF_MAKSURA = "\ufef0"
+let s:a_s_YEH = "\ufef1"
+let s:a_f_YEH = "\ufef2"
+let s:a_i_YEH = "\ufef3"
+let s:a_m_YEH = "\ufef4"
+let s:a_s_LAM_ALEF_MADDA_ABOVE = "\ufef5"
+let s:a_f_LAM_ALEF_MADDA_ABOVE = "\ufef6"
+let s:a_s_LAM_ALEF_HAMZA_ABOVE = "\ufef7"
+let s:a_f_LAM_ALEF_HAMZA_ABOVE = "\ufef8"
+let s:a_s_LAM_ALEF_HAMZA_BELOW = "\ufef9"
+let s:a_f_LAM_ALEF_HAMZA_BELOW = "\ufefa"
+let s:a_s_LAM_ALEF = "\ufefb"
+let s:a_f_LAM_ALEF = "\ufefc"
+
+let s:a_BYTE_ORDER_MARK = "\ufeff"
+
+func Test_shape_initial()
+ new
+ set arabicshape
+
+ " Shaping arabic {testchar} non-arabic Tests chg_c_a2i().
+ " pair[0] = testchar, pair[1] = next-result, pair[2] = current-result
+ for pair in [[s:a_YEH_HAMZA, s:a_f_GHAIN, s:a_i_YEH_HAMZA],
+ \ [s:a_HAMZA, s:a_s_GHAIN, s:a_s_HAMZA],
+ \ [s:a_ALEF_MADDA, s:a_s_GHAIN, s:a_s_ALEF_MADDA],
+ \ [s:a_ALEF_HAMZA_ABOVE, s:a_s_GHAIN, s:a_s_ALEF_HAMZA_ABOVE],
+ \ [s:a_WAW_HAMZA, s:a_s_GHAIN, s:a_s_WAW_HAMZA],
+ \ [s:a_ALEF_HAMZA_BELOW, s:a_s_GHAIN, s:a_s_ALEF_HAMZA_BELOW],
+ \ [s:a_ALEF, s:a_s_GHAIN, s:a_s_ALEF],
+ \ [s:a_TEH_MARBUTA, s:a_s_GHAIN, s:a_s_TEH_MARBUTA],
+ \ [s:a_DAL, s:a_s_GHAIN, s:a_s_DAL],
+ \ [s:a_THAL, s:a_s_GHAIN, s:a_s_THAL],
+ \ [s:a_REH, s:a_s_GHAIN, s:a_s_REH],
+ \ [s:a_ZAIN, s:a_s_GHAIN, s:a_s_ZAIN],
+ \ [s:a_TATWEEL, s:a_f_GHAIN, s:a_TATWEEL],
+ \ [s:a_WAW, s:a_s_GHAIN, s:a_s_WAW],
+ \ [s:a_ALEF_MAKSURA, s:a_s_GHAIN, s:a_s_ALEF_MAKSURA],
+ \ [s:a_BEH, s:a_f_GHAIN, s:a_i_BEH],
+ \ [s:a_TEH, s:a_f_GHAIN, s:a_i_TEH],
+ \ [s:a_THEH, s:a_f_GHAIN, s:a_i_THEH],
+ \ [s:a_JEEM, s:a_f_GHAIN, s:a_i_JEEM],
+ \ [s:a_HAH, s:a_f_GHAIN, s:a_i_HAH],
+ \ [s:a_KHAH, s:a_f_GHAIN, s:a_i_KHAH],
+ \ [s:a_SEEN, s:a_f_GHAIN, s:a_i_SEEN],
+ \ [s:a_SHEEN, s:a_f_GHAIN, s:a_i_SHEEN],
+ \ [s:a_SAD, s:a_f_GHAIN, s:a_i_SAD],
+ \ [s:a_DAD, s:a_f_GHAIN, s:a_i_DAD],
+ \ [s:a_TAH, s:a_f_GHAIN, s:a_i_TAH],
+ \ [s:a_ZAH, s:a_f_GHAIN, s:a_i_ZAH],
+ \ [s:a_AIN, s:a_f_GHAIN, s:a_i_AIN],
+ \ [s:a_GHAIN, s:a_f_GHAIN, s:a_i_GHAIN],
+ \ [s:a_FEH, s:a_f_GHAIN, s:a_i_FEH],
+ \ [s:a_QAF, s:a_f_GHAIN, s:a_i_QAF],
+ \ [s:a_KAF, s:a_f_GHAIN, s:a_i_KAF],
+ \ [s:a_LAM, s:a_f_GHAIN, s:a_i_LAM],
+ \ [s:a_MEEM, s:a_f_GHAIN, s:a_i_MEEM],
+ \ [s:a_NOON, s:a_f_GHAIN, s:a_i_NOON],
+ \ [s:a_HEH, s:a_f_GHAIN, s:a_i_HEH],
+ \ [s:a_YEH, s:a_f_GHAIN, s:a_i_YEH],
+ \ ]
+ call setline(1, s:a_GHAIN . pair[0] . ' ')
+ call assert_equal([pair[1] . pair[2] . ' '], ScreenLines(1, 3))
+ endfor
+
+ set arabicshape&
+ bwipe!
+endfunc
+
+func Test_shape_isolated()
+ new
+ set arabicshape
+
+ " Shaping non-arabic {testchar} non-arabic Tests chg_c_a2s().
+ " pair[0] = testchar, pair[1] = current-result
+ for pair in [[s:a_HAMZA, s:a_s_HAMZA],
+ \ [s:a_ALEF_MADDA, s:a_s_ALEF_MADDA],
+ \ [s:a_ALEF_HAMZA_ABOVE, s:a_s_ALEF_HAMZA_ABOVE],
+ \ [s:a_WAW_HAMZA, s:a_s_WAW_HAMZA],
+ \ [s:a_ALEF_HAMZA_BELOW, s:a_s_ALEF_HAMZA_BELOW],
+ \ [s:a_YEH_HAMZA, s:a_s_YEH_HAMZA],
+ \ [s:a_ALEF, s:a_s_ALEF],
+ \ [s:a_TEH_MARBUTA, s:a_s_TEH_MARBUTA],
+ \ [s:a_DAL, s:a_s_DAL],
+ \ [s:a_THAL, s:a_s_THAL],
+ \ [s:a_REH, s:a_s_REH],
+ \ [s:a_ZAIN, s:a_s_ZAIN],
+ \ [s:a_TATWEEL, s:a_TATWEEL],
+ \ [s:a_WAW, s:a_s_WAW],
+ \ [s:a_ALEF_MAKSURA, s:a_s_ALEF_MAKSURA],
+ \ [s:a_BEH, s:a_s_BEH],
+ \ [s:a_TEH, s:a_s_TEH],
+ \ [s:a_THEH, s:a_s_THEH],
+ \ [s:a_JEEM, s:a_s_JEEM],
+ \ [s:a_HAH, s:a_s_HAH],
+ \ [s:a_KHAH, s:a_s_KHAH],
+ \ [s:a_SEEN, s:a_s_SEEN],
+ \ [s:a_SHEEN, s:a_s_SHEEN],
+ \ [s:a_SAD, s:a_s_SAD],
+ \ [s:a_DAD, s:a_s_DAD],
+ \ [s:a_TAH, s:a_s_TAH],
+ \ [s:a_ZAH, s:a_s_ZAH],
+ \ [s:a_AIN, s:a_s_AIN],
+ \ [s:a_GHAIN, s:a_s_GHAIN],
+ \ [s:a_FEH, s:a_s_FEH],
+ \ [s:a_QAF, s:a_s_QAF],
+ \ [s:a_KAF, s:a_s_KAF],
+ \ [s:a_LAM, s:a_s_LAM],
+ \ [s:a_MEEM, s:a_s_MEEM],
+ \ [s:a_NOON, s:a_s_NOON],
+ \ [s:a_HEH, s:a_s_HEH],
+ \ [s:a_YEH, s:a_s_YEH],
+ \ ]
+ call setline(1, ' ' . pair[0] . ' ')
+ call assert_equal([' ' . pair[1] . ' '], ScreenLines(1, 3))
+ endfor
+
+ set arabicshape&
+ bwipe!
+endfunc
+
+func Test_shape_medial()
+ new
+ set arabicshape
+
+ " Shaping arabic {testchar} arabic Tests chg_c_a2m().
+ " pair[0] = testchar, pair[1] = next-result, pair[2] = current-result,
+ " pair[3] = previous-result
+ for pair in [[s:a_HAMZA, s:a_s_GHAIN, s:a_s_HAMZA, s:a_s_BEH],
+ \[s:a_ALEF_MADDA, s:a_s_GHAIN, s:a_f_ALEF_MADDA, s:a_i_BEH],
+ \[s:a_ALEF_HAMZA_ABOVE, s:a_s_GHAIN, s:a_f_ALEF_HAMZA_ABOVE, s:a_i_BEH],
+ \[s:a_WAW_HAMZA, s:a_s_GHAIN, s:a_f_WAW_HAMZA, s:a_i_BEH],
+ \[s:a_ALEF_HAMZA_BELOW, s:a_s_GHAIN, s:a_f_ALEF_HAMZA_BELOW, s:a_i_BEH],
+ \[s:a_YEH_HAMZA, s:a_f_GHAIN, s:a_m_YEH_HAMZA, s:a_i_BEH],
+ \[s:a_ALEF, s:a_s_GHAIN, s:a_f_ALEF, s:a_i_BEH],
+ \[s:a_BEH, s:a_f_GHAIN, s:a_m_BEH, s:a_i_BEH],
+ \[s:a_TEH_MARBUTA, s:a_s_GHAIN, s:a_f_TEH_MARBUTA, s:a_i_BEH],
+ \[s:a_TEH, s:a_f_GHAIN, s:a_m_TEH, s:a_i_BEH],
+ \[s:a_THEH, s:a_f_GHAIN, s:a_m_THEH, s:a_i_BEH],
+ \[s:a_JEEM, s:a_f_GHAIN, s:a_m_JEEM, s:a_i_BEH],
+ \[s:a_HAH, s:a_f_GHAIN, s:a_m_HAH, s:a_i_BEH],
+ \[s:a_KHAH, s:a_f_GHAIN, s:a_m_KHAH, s:a_i_BEH],
+ \[s:a_DAL, s:a_s_GHAIN, s:a_f_DAL, s:a_i_BEH],
+ \[s:a_THAL, s:a_s_GHAIN, s:a_f_THAL, s:a_i_BEH],
+ \[s:a_REH, s:a_s_GHAIN, s:a_f_REH, s:a_i_BEH],
+ \[s:a_ZAIN, s:a_s_GHAIN, s:a_f_ZAIN, s:a_i_BEH],
+ \[s:a_SEEN, s:a_f_GHAIN, s:a_m_SEEN, s:a_i_BEH],
+ \[s:a_SHEEN, s:a_f_GHAIN, s:a_m_SHEEN, s:a_i_BEH],
+ \[s:a_SAD, s:a_f_GHAIN, s:a_m_SAD, s:a_i_BEH],
+ \[s:a_DAD, s:a_f_GHAIN, s:a_m_DAD, s:a_i_BEH],
+ \[s:a_TAH, s:a_f_GHAIN, s:a_m_TAH, s:a_i_BEH],
+ \[s:a_ZAH, s:a_f_GHAIN, s:a_m_ZAH, s:a_i_BEH],
+ \[s:a_AIN, s:a_f_GHAIN, s:a_m_AIN, s:a_i_BEH],
+ \[s:a_GHAIN, s:a_f_GHAIN, s:a_m_GHAIN, s:a_i_BEH],
+ \[s:a_TATWEEL, s:a_f_GHAIN, s:a_TATWEEL, s:a_i_BEH],
+ \[s:a_FEH, s:a_f_GHAIN, s:a_m_FEH, s:a_i_BEH],
+ \[s:a_QAF, s:a_f_GHAIN, s:a_m_QAF, s:a_i_BEH],
+ \[s:a_KAF, s:a_f_GHAIN, s:a_m_KAF, s:a_i_BEH],
+ \[s:a_LAM, s:a_f_GHAIN, s:a_m_LAM, s:a_i_BEH],
+ \[s:a_MEEM, s:a_f_GHAIN, s:a_m_MEEM, s:a_i_BEH],
+ \[s:a_NOON, s:a_f_GHAIN, s:a_m_NOON, s:a_i_BEH],
+ \[s:a_HEH, s:a_f_GHAIN, s:a_m_HEH, s:a_i_BEH],
+ \[s:a_WAW, s:a_s_GHAIN, s:a_f_WAW, s:a_i_BEH],
+ \[s:a_ALEF_MAKSURA, s:a_s_GHAIN, s:a_f_ALEF_MAKSURA, s:a_i_BEH],
+ \[s:a_YEH, s:a_f_GHAIN, s:a_m_YEH, s:a_i_BEH],
+ \ ]
+ call setline(1, s:a_GHAIN . pair[0] . s:a_BEH)
+ call assert_equal([pair[1] . pair[2] . pair[3]], ScreenLines(1, 3))
+ endfor
+
+ set arabicshape&
+ bwipe!
+endfunc
+
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index dc9790a39c..115c228ce8 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -25,6 +25,26 @@ func Test_complete_wildmenu()
set nowildmenu
endfunc
+func Test_map_completion()
+ if !has('cmdline_compl')
+ return
+ endif
+ call feedkeys(":map <unique> <si\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <unique> <silent>', getreg(':'))
+ call feedkeys(":map <script> <un\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <script> <unique>', getreg(':'))
+ call feedkeys(":map <expr> <sc\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <expr> <script>', getreg(':'))
+ call feedkeys(":map <buffer> <e\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <buffer> <expr>', getreg(':'))
+ call feedkeys(":map <nowait> <b\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <nowait> <buffer>', getreg(':'))
+ call feedkeys(":map <special> <no\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <special> <nowait>', getreg(':'))
+ call feedkeys(":map <silent> <sp\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"map <silent> <special>', getreg(':'))
+endfunc
+
func Test_expr_completion()
if !(has('cmdline_compl') && has('eval'))
return
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 8ee82bd538..33617c9cbe 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -272,3 +272,98 @@ func Test_setting_cursor()
call delete('Xtest1')
call delete('Xtest2')
endfunc
+
+func Test_diff_move_to()
+ new
+ call setline(1, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
+ diffthis
+ vnew
+ call setline(1, [1, '2x', 3, 4, 4, 5, '6x', 7, '8x', 9, '10x'])
+ diffthis
+ norm ]c
+ call assert_equal(2, line('.'))
+ norm 3]c
+ call assert_equal(9, line('.'))
+ norm 10]c
+ call assert_equal(11, line('.'))
+ norm [c
+ call assert_equal(9, line('.'))
+ norm 2[c
+ call assert_equal(5, line('.'))
+ norm 10[c
+ call assert_equal(2, line('.'))
+ %bwipe!
+endfunc
+
+func Test_diffpatch()
+ " The patch program on MS-Windows may fail or hang.
+ if !executable('patch') || !has('unix')
+ return
+ endif
+ new
+ insert
+***************
+*** 1,3 ****
+ 1
+! 2
+ 3
+--- 1,4 ----
+ 1
+! 2x
+ 3
++ 4
+.
+ saveas Xpatch
+ bwipe!
+ new
+ call assert_fails('diffpatch Xpatch', 'E816:')
+ call setline(1, ['1', '2', '3'])
+ diffpatch Xpatch
+ call assert_equal(['1', '2x', '3', '4'], getline(1, '$'))
+ call delete('Xpatch')
+ bwipe!
+endfunc
+
+func Test_diff_too_many_buffers()
+ for i in range(1, 8)
+ exe "new Xtest" . i
+ diffthis
+ endfor
+ new Xtest9
+ call assert_fails('diffthis', 'E96:')
+ %bwipe!
+endfunc
+
+func Test_diff_nomodifiable()
+ new
+ call setline(1, [1, 2, 3, 4])
+ setl nomodifiable
+ diffthis
+ vnew
+ call setline(1, ['1x', 2, 3, 3, 4])
+ diffthis
+ call assert_fails('norm dp', 'E793:')
+ setl nomodifiable
+ call assert_fails('norm do', 'E21:')
+ %bwipe!
+endfunc
+
+func Test_diff_lastline()
+ enew!
+ only!
+ call setline(1, ['This is a ', 'line with five ', 'rows'])
+ diffthis
+ botright vert new
+ call setline(1, ['This is', 'a line with ', 'four rows'])
+ diffthis
+ 1
+ call feedkeys("Je a\<CR>", 'tx')
+ call feedkeys("Je a\<CR>", 'tx')
+ let w1lines = winline()
+ wincmd w
+ $
+ let w2lines = winline()
+ call assert_equal(w2lines, w1lines)
+ bwipe!
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim
index 980d67d49d..56a4cc9b31 100644
--- a/src/nvim/testdir/test_listlbr_utf8.vim
+++ b/src/nvim/testdir/test_listlbr_utf8.vim
@@ -194,6 +194,33 @@ func Test_multibyte_sign_and_colorcolumn()
call s:close_windows()
endfunc
+func Test_illegal_byte_and_breakat()
+ call s:test_windows("setl sbr= brk+=<")
+ vert resize 18
+ call setline(1, repeat("\x80", 6))
+ redraw!
+ let lines = s:screen_lines([1, 2], winwidth(0))
+ let expect = [
+\ "<80><80><80><80><8",
+\ "0><80> ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('setl brk&vim')
+endfunc
+
+func Test_multibyte_wrap_and_breakat()
+ call s:test_windows("setl sbr= brk+=>")
+ call setline(1, repeat('a', 17) . repeat('あ', 2))
+ redraw!
+ let lines = s:screen_lines([1, 2], winwidth(0))
+ let expect = [
+\ "aaaaaaaaaaaaaaaaaあ>",
+\ "あ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('setl brk&vim')
+endfunc
+
func Test_chinese_char_on_wrap_column()
call s:test_windows("setl nolbr wrap sbr=")
syntax off
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 4747d5704d..8635daa7ad 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -1,5 +1,7 @@
" Test for various Normal mode commands
+source shared.vim
+
func! Setup_NewWindow()
10new
call setline(1, range(1,100))
@@ -1069,10 +1071,10 @@ func! Test_normal18_z_fold()
endfunc
func! Test_normal19_z_spell()
- throw "skipped: Nvim 'spell' requires download"
if !has("spell") || !has('syntax')
return
endif
+ " let $TMPDIR=fnamemodify($TMPDIR, ':.')
new
call append(0, ['1 good', '2 goood', '3 goood'])
set spell spellfile=./Xspellfile.add spelllang=en
@@ -1119,7 +1121,9 @@ func! Test_normal19_z_spell()
" Test for zG
let a=execute('unsilent norm! V$zG')
call assert_match("Word '2 goood' added to .*", a)
+ set shortmess=
let fname=matchstr(a, 'to\s\+\zs\f\+$')
+ let fname=Fix_truncated_tmpfile(fname)
let cnt=readfile(fname)
call assert_equal('2 goood', cnt[0])
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index a333e7f206..03112df46f 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -298,3 +298,10 @@ func Test_searchpair()
q!
endfunc
+func Test_searchc()
+ " These commands used to cause memory overflow in searchc().
+ new
+ norm ixx
+ exe "norm 0t\u93cf"
+ bw!
+endfunc
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
new file mode 100644
index 0000000000..21f2363731
--- /dev/null
+++ b/src/nvim/testdir/test_spell.vim
@@ -0,0 +1,821 @@
+" Test spell checking
+
+if !has('spell')
+ finish
+endif
+
+func TearDown()
+ set nospell
+ call delete('Xtest.aff')
+ call delete('Xtest.dic')
+ call delete('Xtest.latin1.add')
+ call delete('Xtest.latin1.add.spl')
+ call delete('Xtest.latin1.spl')
+ call delete('Xtest.latin1.sug')
+endfunc
+
+func Test_wrap_search()
+ new
+ call setline(1, ['The', '', 'A plong line with two zpelling mistakes', '', 'End'])
+ set spell wrapscan
+ normal ]s
+ call assert_equal('plong', expand('<cword>'))
+ normal ]s
+ call assert_equal('zpelling', expand('<cword>'))
+ normal ]s
+ call assert_equal('plong', expand('<cword>'))
+ bwipe!
+ set nospell
+endfunc
+
+func Test_curswant()
+ new
+ call setline(1, ['Another plong line', 'abcdefghijklmnopq'])
+ set spell wrapscan
+ normal 0]s
+ call assert_equal('plong', expand('<cword>'))
+ normal j
+ call assert_equal(9, getcurpos()[2])
+ normal 0[s
+ call assert_equal('plong', expand('<cword>'))
+ normal j
+ call assert_equal(9, getcurpos()[2])
+
+ normal 0]S
+ call assert_equal('plong', expand('<cword>'))
+ normal j
+ call assert_equal(9, getcurpos()[2])
+ normal 0[S
+ call assert_equal('plong', expand('<cword>'))
+ normal j
+ call assert_equal(9, getcurpos()[2])
+
+ normal 1G0
+ call assert_equal('plong', spellbadword()[0])
+ normal j
+ call assert_equal(9, getcurpos()[2])
+
+ bwipe!
+ set nospell
+endfunc
+
+func Test_z_equal_on_invalid_utf8_word()
+ split
+ set spell
+ call setline(1, "\xff")
+ norm z=
+ set nospell
+ bwipe!
+endfunc
+
+func Test_spellreall()
+ new
+ set spell
+ call assert_fails('spellrepall', 'E752:')
+ call setline(1, ['A speling mistake. The same speling mistake.',
+ \ 'Another speling mistake.'])
+ call feedkeys(']s1z=', 'tx')
+ call assert_equal('A spelling mistake. The same speling mistake.', getline(1))
+ call assert_equal('Another speling mistake.', getline(2))
+ spellrepall
+ call assert_equal('A spelling mistake. The same spelling mistake.', getline(1))
+ call assert_equal('Another spelling mistake.', getline(2))
+ call assert_fails('spellrepall', 'E753:')
+ set spell&
+ bwipe!
+endfunc
+
+func Test_zz_basic()
+ call LoadAffAndDic(g:test_data_aff1, g:test_data_dic1)
+ call RunGoodBad("wrong OK puts. Test the end",
+ \ "bad: inputs comment ok Ok. test d\xE9\xF4l end the",
+ \["Comment", "deol", "d\xE9\xF4r", "input", "OK", "output", "outputs", "outtest", "put", "puts",
+ \ "test", "testen", "testn", "the end", "uk", "wrong"],
+ \[
+ \ ["bad", ["put", "uk", "OK"]],
+ \ ["inputs", ["input", "puts", "outputs"]],
+ \ ["comment", ["Comment", "outtest", "the end"]],
+ \ ["ok", ["OK", "uk", "put"]],
+ \ ["Ok", ["OK", "Uk", "Put"]],
+ \ ["test", ["Test", "testn", "testen"]],
+ \ ["d\xE9\xF4l", ["deol", "d\xE9\xF4r", "test"]],
+ \ ["end", ["put", "uk", "test"]],
+ \ ["the", ["put", "uk", "test"]],
+ \ ]
+ \ )
+
+ call assert_equal("gebletegek", soundfold('goobledygoook'))
+ call assert_equal("kepereneven", soundfold('koprnven'))
+ call assert_equal("everles gesvets etele", soundfold('oeverloos gezwets edale'))
+endfunc
+
+" Postponed prefixes
+func Test_zz_prefixes()
+ call LoadAffAndDic(g:test_data_aff2, g:test_data_dic1)
+ call RunGoodBad("puts",
+ \ "bad: inputs comment ok Ok end the. test d\xE9\xF4l",
+ \ ["Comment", "deol", "d\xE9\xF4r", "OK", "put", "input", "output", "puts", "outputs", "test", "outtest", "testen", "testn", "the end", "uk", "wrong"],
+ \ [
+ \ ["bad", ["put", "uk", "OK"]],
+ \ ["inputs", ["input", "puts", "outputs"]],
+ \ ["comment", ["Comment"]],
+ \ ["ok", ["OK", "uk", "put"]],
+ \ ["Ok", ["OK", "Uk", "Put"]],
+ \ ["end", ["put", "uk", "deol"]],
+ \ ["the", ["put", "uk", "test"]],
+ \ ["test", ["Test", "testn", "testen"]],
+ \ ["d\xE9\xF4l", ["deol", "d\xE9\xF4r", "test"]],
+ \ ])
+endfunc
+
+"Compound words
+func Test_zz_compound()
+ call LoadAffAndDic(g:test_data_aff3, g:test_data_dic3)
+ call RunGoodBad("foo m\xEF foobar foofoobar barfoo barbarfoo",
+ \ "bad: bar la foom\xEF barm\xEF m\xEFfoo m\xEFbar m\xEFm\xEF lala m\xEFla lam\xEF foola labar",
+ \ ["foo", "m\xEF"],
+ \ [
+ \ ["bad", ["foo", "m\xEF"]],
+ \ ["bar", ["barfoo", "foobar", "foo"]],
+ \ ["la", ["m\xEF", "foo"]],
+ \ ["foom\xEF", ["foo m\xEF", "foo", "foofoo"]],
+ \ ["barm\xEF", ["barfoo", "m\xEF", "barbar"]],
+ \ ["m\xEFfoo", ["m\xEF foo", "foo", "foofoo"]],
+ \ ["m\xEFbar", ["foobar", "barbar", "m\xEF"]],
+ \ ["m\xEFm\xEF", ["m\xEF m\xEF", "m\xEF"]],
+ \ ["lala", []],
+ \ ["m\xEFla", ["m\xEF", "m\xEF m\xEF"]],
+ \ ["lam\xEF", ["m\xEF", "m\xEF m\xEF"]],
+ \ ["foola", ["foo", "foobar", "foofoo"]],
+ \ ["labar", ["barbar", "foobar"]],
+ \ ])
+
+ call LoadAffAndDic(g:test_data_aff4, g:test_data_dic4)
+ call RunGoodBad("word util bork prebork start end wordutil wordutils pro-ok bork borkbork borkborkbork borkborkborkbork borkborkborkborkbork tomato tomatotomato startend startword startwordword startwordend startwordwordend startwordwordwordend prebork preborkbork preborkborkbork nouword",
+ \ "bad: wordutilize pro borkborkborkborkborkbork tomatotomatotomato endstart endend startstart wordend wordstart preborkprebork preborkpreborkbork startwordwordwordwordend borkpreborkpreborkbork utilsbork startnouword",
+ \ ["bork", "prebork", "end", "pro-ok", "start", "tomato", "util", "utilize", "utils", "word", "nouword"],
+ \ [
+ \ ["bad", ["end", "bork", "word"]],
+ \ ["wordutilize", ["word utilize", "wordutils", "wordutil"]],
+ \ ["pro", ["bork", "word", "end"]],
+ \ ["borkborkborkborkborkbork", ["bork borkborkborkborkbork", "borkbork borkborkborkbork", "borkborkbork borkborkbork"]],
+ \ ["tomatotomatotomato", ["tomato tomatotomato", "tomatotomato tomato", "tomato tomato tomato"]],
+ \ ["endstart", ["end start", "start"]],
+ \ ["endend", ["end end", "end"]],
+ \ ["startstart", ["start start"]],
+ \ ["wordend", ["word end", "word", "wordword"]],
+ \ ["wordstart", ["word start", "bork start"]],
+ \ ["preborkprebork", ["prebork prebork", "preborkbork", "preborkborkbork"]],
+ \ ["preborkpreborkbork", ["prebork preborkbork", "preborkborkbork", "preborkborkborkbork"]],
+ \ ["startwordwordwordwordend", ["startwordwordwordword end", "startwordwordwordword", "start wordwordwordword end"]],
+ \ ["borkpreborkpreborkbork", ["bork preborkpreborkbork", "bork prebork preborkbork", "bork preborkprebork bork"]],
+ \ ["utilsbork", ["utilbork", "utils bork", "util bork"]],
+ \ ["startnouword", ["start nouword", "startword", "startborkword"]],
+ \ ])
+
+endfunc
+
+"Test affix flags with two characters
+func Test_zz_affix()
+ call LoadAffAndDic(g:test_data_aff5, g:test_data_dic5)
+ call RunGoodBad("fooa1 fooa\xE9 bar prebar barbork prebarbork startprebar start end startend startmiddleend nouend",
+ \ "bad: foo fooa2 prabar probarbirk middle startmiddle middleend endstart startprobar startnouend",
+ \ ["bar", "barbork", "end", "fooa1", "fooa\xE9", "nouend", "prebar", "prebarbork", "start"],
+ \ [
+ \ ["bad", ["bar", "end", "fooa1"]],
+ \ ["foo", ["fooa1", "fooa\xE9", "bar"]],
+ \ ["fooa2", ["fooa1", "fooa\xE9", "bar"]],
+ \ ["prabar", ["prebar", "bar", "bar bar"]],
+ \ ["probarbirk", ["prebarbork"]],
+ \ ["middle", []],
+ \ ["startmiddle", ["startmiddleend", "startmiddlebar"]],
+ \ ["middleend", []],
+ \ ["endstart", ["end start", "start"]],
+ \ ["startprobar", ["startprebar", "start prebar", "startbar"]],
+ \ ["startnouend", ["start nouend", "startend"]],
+ \ ])
+
+ call LoadAffAndDic(g:test_data_aff6, g:test_data_dic6)
+ call RunGoodBad("meea1 meea\xE9 bar prebar barbork prebarbork leadprebar lead end leadend leadmiddleend",
+ \ "bad: mee meea2 prabar probarbirk middle leadmiddle middleend endlead leadprobar",
+ \ ["bar", "barbork", "end", "lead", "meea1", "meea\xE9", "prebar", "prebarbork"],
+ \ [
+ \ ["bad", ["bar", "end", "lead"]],
+ \ ["mee", ["meea1", "meea\xE9", "bar"]],
+ \ ["meea2", ["meea1", "meea\xE9", "lead"]],
+ \ ["prabar", ["prebar", "bar", "leadbar"]],
+ \ ["probarbirk", ["prebarbork"]],
+ \ ["middle", []],
+ \ ["leadmiddle", ["leadmiddleend", "leadmiddlebar"]],
+ \ ["middleend", []],
+ \ ["endlead", ["end lead", "lead", "end end"]],
+ \ ["leadprobar", ["leadprebar", "lead prebar", "leadbar"]],
+ \ ])
+
+ call LoadAffAndDic(g:test_data_aff7, g:test_data_dic7)
+ call RunGoodBad("meea1 meea\xE9 bar prebar barmeat prebarmeat leadprebar lead tail leadtail leadmiddletail",
+ \ "bad: mee meea2 prabar probarmaat middle leadmiddle middletail taillead leadprobar",
+ \ ["bar", "barmeat", "lead", "meea1", "meea\xE9", "prebar", "prebarmeat", "tail"],
+ \ [
+ \ ["bad", ["bar", "lead", "tail"]],
+ \ ["mee", ["meea1", "meea\xE9", "bar"]],
+ \ ["meea2", ["meea1", "meea\xE9", "lead"]],
+ \ ["prabar", ["prebar", "bar", "leadbar"]],
+ \ ["probarmaat", ["prebarmeat"]],
+ \ ["middle", []],
+ \ ["leadmiddle", ["leadmiddlebar"]],
+ \ ["middletail", []],
+ \ ["taillead", ["tail lead", "tail"]],
+ \ ["leadprobar", ["leadprebar", "lead prebar", "leadbar"]],
+ \ ])
+endfunc
+
+func Test_zz_NOSLITSUGS()
+ call LoadAffAndDic(g:test_data_aff8, g:test_data_dic8)
+ call RunGoodBad("foo bar faabar", "bad: foobar barfoo",
+ \ ["bar", "faabar", "foo"],
+ \ [
+ \ ["bad", ["bar", "foo"]],
+ \ ["foobar", ["faabar", "foo bar", "bar"]],
+ \ ["barfoo", ["bar foo", "bar", "foo"]],
+ \ ])
+endfunc
+
+" Numbers
+func Test_zz_Numbers()
+ call LoadAffAndDic(g:test_data_aff9, g:test_data_dic9)
+ call RunGoodBad("0b1011 0777 1234 0x01ff", "",
+ \ ["bar", "foo"],
+ \ [
+ \ ])
+endfunc
+
+function FirstSpellWord()
+ call feedkeys("/^start:\n", 'tx')
+ normal ]smm
+ let [str, a] = spellbadword()
+ return str
+endfunc
+
+function SecondSpellWord()
+ normal `m]s
+ let [str, a] = spellbadword()
+ return str
+endfunc
+
+"Test with SAL instead of SOFO items; test automatic reloading
+func Test_zz_sal_and_addition()
+ throw 'skipped: Nvim does not support enc=latin1'
+ set enc=latin1
+ set spellfile=
+ call writefile(g:test_data_dic1, "Xtest.dic")
+ call writefile(g:test_data_aff_sal, "Xtest.aff")
+ mkspell! Xtest Xtest
+ set spl=Xtest.latin1.spl spell
+ call assert_equal('kbltykk', soundfold('goobledygoook'))
+ call assert_equal('kprnfn', soundfold('koprnven'))
+ call assert_equal('*fls kswts tl', soundfold('oeverloos gezwets edale'))
+
+ "also use an addition file
+ call writefile(["/regions=usgbnz", "elequint/2", "elekwint/3"], "Xtest.latin1.add")
+ mkspell! Xtest.latin1.add.spl Xtest.latin1.add
+
+ bwipe!
+ call setline(1, ["start: elequint test elekwint test elekwent asdf"])
+
+ set spellfile=Xtest.latin1.add
+ call assert_equal("elekwent", FirstSpellWord())
+
+ set spl=Xtest_us.latin1.spl
+ call assert_equal("elequint", FirstSpellWord())
+ call assert_equal("elekwint", SecondSpellWord())
+
+ set spl=Xtest_gb.latin1.spl
+ call assert_equal("elekwint", FirstSpellWord())
+ call assert_equal("elekwent", SecondSpellWord())
+
+ set spl=Xtest_nz.latin1.spl
+ call assert_equal("elequint", FirstSpellWord())
+ call assert_equal("elekwent", SecondSpellWord())
+
+ set spl=Xtest_ca.latin1.spl
+ call assert_equal("elequint", FirstSpellWord())
+ call assert_equal("elekwint", SecondSpellWord())
+endfunc
+
+func Test_region_error()
+ messages clear
+ call writefile(["/regions=usgbnz", "elequint/0"], "Xtest.latin1.add")
+ mkspell! Xtest.latin1.add.spl Xtest.latin1.add
+ call assert_match('Invalid region nr in Xtest.latin1.add line 2: 0', execute('messages'))
+ call delete('Xtest.latin1.add')
+ call delete('Xtest.latin1.add.spl')
+endfunc
+
+" Check using z= in new buffer (crash fixed by patch 7.4a.028).
+func Test_zeq_crash()
+ new
+ set maxmem=512 spell
+ call feedkeys('iasdz=:\"', 'tx')
+
+ bwipe!
+endfunc
+
+func LoadAffAndDic(aff_contents, dic_contents)
+ throw 'skipped: Nvim does not support enc=latin1'
+ set enc=latin1
+ set spellfile=
+ call writefile(a:aff_contents, "Xtest.aff")
+ call writefile(a:dic_contents, "Xtest.dic")
+ " Generate a .spl file from a .dic and .aff file.
+ mkspell! Xtest Xtest
+ " use that spell file
+ set spl=Xtest.latin1.spl spell
+endfunc
+
+func ListWords()
+ spelldump
+ %yank
+ quit
+ return split(@", "\n")
+endfunc
+
+func TestGoodBadBase()
+ exe '1;/^good:'
+ normal 0f:]s
+ let prevbad = ''
+ let result = []
+ while 1
+ let [bad, a] = spellbadword()
+ if bad == '' || bad == prevbad || bad == 'badend'
+ break
+ endif
+ let prevbad = bad
+ let lst = spellsuggest(bad, 3)
+ normal mm
+
+ call add(result, [bad, lst])
+ normal `m]s
+ endwhile
+ return result
+endfunc
+
+func RunGoodBad(good, bad, expected_words, expected_bad_words)
+ bwipe!
+ call setline(1, ["good: ", a:good, a:bad, " badend "])
+ let words = ListWords()
+ call assert_equal(a:expected_words, words[1:-1])
+ let bad_words = TestGoodBadBase()
+ call assert_equal(a:expected_bad_words, bad_words)
+ bwipe!
+endfunc
+
+let g:test_data_aff1 = [
+ \"SET ISO8859-1",
+ \"TRY esianrtolcdugmphbyfvkwjkqxz-\xEB\xE9\xE8\xEA\xEF\xEE\xE4\xE0\xE2\xF6\xFC\xFB'ESIANRTOLCDUGMPHBYFVKWJKQXZ",
+ \"",
+ \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF",
+ \"",
+ \"SOFOFROM abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xBF",
+ \"SOFOTO ebctefghejklnnepkrstevvkesebctefghejklnnepkrstevvkeseeeeeeeceeeeeeeedneeeeeeeeeeepseeeeeeeeceeeeeeeedneeeeeeeeeeep?",
+ \"",
+ \"MIDWORD\t'-",
+ \"",
+ \"KEP =",
+ \"RAR ?",
+ \"BAD !",
+ \"",
+ \"PFX I N 1",
+ \"PFX I 0 in .",
+ \"",
+ \"PFX O Y 1",
+ \"PFX O 0 out .",
+ \"",
+ \"SFX S Y 2",
+ \"SFX S 0 s [^s]",
+ \"SFX S 0 es s",
+ \"",
+ \"SFX N N 3",
+ \"SFX N 0 en [^n]",
+ \"SFX N 0 nen n",
+ \"SFX N 0 n .",
+ \"",
+ \"REP 3",
+ \"REP g ch",
+ \"REP ch g",
+ \"REP svp s.v.p.",
+ \"",
+ \"MAP 9",
+ \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5",
+ \"MAP e\xE8\xE9\xEA\xEB",
+ \"MAP i\xEC\xED\xEE\xEF",
+ \"MAP o\xF2\xF3\xF4\xF5\xF6",
+ \"MAP u\xF9\xFA\xFB\xFC",
+ \"MAP n\xF1",
+ \"MAP c\xE7",
+ \"MAP y\xFF\xFD",
+ \"MAP s\xDF",
+ \ ]
+let g:test_data_dic1 = [
+ \"123456",
+ \"test/NO",
+ \"# comment",
+ \"wrong",
+ \"Comment",
+ \"OK",
+ \"uk",
+ \"put/ISO",
+ \"the end",
+ \"deol",
+ \"d\xE9\xF4r",
+ \ ]
+let g:test_data_aff2 = [
+ \"SET ISO8859-1",
+ \"",
+ \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF",
+ \"",
+ \"PFXPOSTPONE",
+ \"",
+ \"MIDWORD\t'-",
+ \"",
+ \"KEP =",
+ \"RAR ?",
+ \"BAD !",
+ \"",
+ \"PFX I N 1",
+ \"PFX I 0 in .",
+ \"",
+ \"PFX O Y 1",
+ \"PFX O 0 out [a-z]",
+ \"",
+ \"SFX S Y 2",
+ \"SFX S 0 s [^s]",
+ \"SFX S 0 es s",
+ \"",
+ \"SFX N N 3",
+ \"SFX N 0 en [^n]",
+ \"SFX N 0 nen n",
+ \"SFX N 0 n .",
+ \"",
+ \"REP 3",
+ \"REP g ch",
+ \"REP ch g",
+ \"REP svp s.v.p.",
+ \"",
+ \"MAP 9",
+ \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5",
+ \"MAP e\xE8\xE9\xEA\xEB",
+ \"MAP i\xEC\xED\xEE\xEF",
+ \"MAP o\xF2\xF3\xF4\xF5\xF6",
+ \"MAP u\xF9\xFA\xFB\xFC",
+ \"MAP n\xF1",
+ \"MAP c\xE7",
+ \"MAP y\xFF\xFD",
+ \"MAP s\xDF",
+ \ ]
+let g:test_data_aff3 = [
+ \"SET ISO8859-1",
+ \"",
+ \"COMPOUNDMIN 3",
+ \"COMPOUNDRULE m*",
+ \"NEEDCOMPOUND x",
+ \ ]
+let g:test_data_dic3 = [
+ \"1234",
+ \"foo/m",
+ \"bar/mx",
+ \"m\xEF/m",
+ \"la/mx",
+ \ ]
+let g:test_data_aff4 = [
+ \"SET ISO8859-1",
+ \"",
+ \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF",
+ \"",
+ \"COMPOUNDRULE m+",
+ \"COMPOUNDRULE sm*e",
+ \"COMPOUNDRULE sm+",
+ \"COMPOUNDMIN 3",
+ \"COMPOUNDWORDMAX 3",
+ \"COMPOUNDFORBIDFLAG t",
+ \"",
+ \"COMPOUNDSYLMAX 5",
+ \"SYLLABLE a\xE1e\xE9i\xEDo\xF3\xF6\xF5u\xFA\xFC\xFBy/aa/au/ea/ee/ei/ie/oa/oe/oo/ou/uu/ui",
+ \"",
+ \"MAP 9",
+ \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5",
+ \"MAP e\xE8\xE9\xEA\xEB",
+ \"MAP i\xEC\xED\xEE\xEF",
+ \"MAP o\xF2\xF3\xF4\xF5\xF6",
+ \"MAP u\xF9\xFA\xFB\xFC",
+ \"MAP n\xF1",
+ \"MAP c\xE7",
+ \"MAP y\xFF\xFD",
+ \"MAP s\xDF",
+ \"",
+ \"NEEDAFFIX x",
+ \"",
+ \"PFXPOSTPONE",
+ \"",
+ \"MIDWORD '-",
+ \"",
+ \"SFX q N 1",
+ \"SFX q 0 -ok .",
+ \"",
+ \"SFX a Y 2",
+ \"SFX a 0 s .",
+ \"SFX a 0 ize/t .",
+ \"",
+ \"PFX p N 1",
+ \"PFX p 0 pre .",
+ \"",
+ \"PFX P N 1",
+ \"PFX P 0 nou .",
+ \ ]
+let g:test_data_dic4 = [
+ \"1234",
+ \"word/mP",
+ \"util/am",
+ \"pro/xq",
+ \"tomato/m",
+ \"bork/mp",
+ \"start/s",
+ \"end/e",
+ \ ]
+let g:test_data_aff5 = [
+ \"SET ISO8859-1",
+ \"",
+ \"FLAG long",
+ \"",
+ \"NEEDAFFIX !!",
+ \"",
+ \"COMPOUNDRULE ssmm*ee",
+ \"",
+ \"NEEDCOMPOUND xx",
+ \"COMPOUNDPERMITFLAG pp",
+ \"",
+ \"SFX 13 Y 1",
+ \"SFX 13 0 bork .",
+ \"",
+ \"SFX a1 Y 1",
+ \"SFX a1 0 a1 .",
+ \"",
+ \"SFX a\xE9 Y 1",
+ \"SFX a\xE9 0 a\xE9 .",
+ \"",
+ \"PFX zz Y 1",
+ \"PFX zz 0 pre/pp .",
+ \"",
+ \"PFX yy Y 1",
+ \"PFX yy 0 nou .",
+ \ ]
+let g:test_data_dic5 = [
+ \"1234",
+ \"foo/a1a\xE9!!",
+ \"bar/zz13ee",
+ \"start/ss",
+ \"end/eeyy",
+ \"middle/mmxx",
+ \ ]
+let g:test_data_aff6 = [
+ \"SET ISO8859-1",
+ \"",
+ \"FLAG caplong",
+ \"",
+ \"NEEDAFFIX A!",
+ \"",
+ \"COMPOUNDRULE sMm*Ee",
+ \"",
+ \"NEEDCOMPOUND Xx",
+ \"",
+ \"COMPOUNDPERMITFLAG p",
+ \"",
+ \"SFX N3 Y 1",
+ \"SFX N3 0 bork .",
+ \"",
+ \"SFX A1 Y 1",
+ \"SFX A1 0 a1 .",
+ \"",
+ \"SFX A\xE9 Y 1",
+ \"SFX A\xE9 0 a\xE9 .",
+ \"",
+ \"PFX Zz Y 1",
+ \"PFX Zz 0 pre/p .",
+ \ ]
+let g:test_data_dic6 = [
+ \"1234",
+ \"mee/A1A\xE9A!",
+ \"bar/ZzN3Ee",
+ \"lead/s",
+ \"end/Ee",
+ \"middle/MmXx",
+ \ ]
+let g:test_data_aff7 = [
+ \"SET ISO8859-1",
+ \"",
+ \"FLAG num",
+ \"",
+ \"NEEDAFFIX 9999",
+ \"",
+ \"COMPOUNDRULE 2,77*123",
+ \"",
+ \"NEEDCOMPOUND 1",
+ \"COMPOUNDPERMITFLAG 432",
+ \"",
+ \"SFX 61003 Y 1",
+ \"SFX 61003 0 meat .",
+ \"",
+ \"SFX 391 Y 1",
+ \"SFX 391 0 a1 .",
+ \"",
+ \"SFX 111 Y 1",
+ \"SFX 111 0 a\xE9 .",
+ \"",
+ \"PFX 17 Y 1",
+ \"PFX 17 0 pre/432 .",
+ \ ]
+let g:test_data_dic7 = [
+ \"1234",
+ \"mee/391,111,9999",
+ \"bar/17,61003,123",
+ \"lead/2",
+ \"tail/123",
+ \"middle/77,1",
+ \ ]
+let g:test_data_aff8 = [
+ \"SET ISO8859-1",
+ \"",
+ \"NOSPLITSUGS",
+ \ ]
+let g:test_data_dic8 = [
+ \"1234",
+ \"foo",
+ \"bar",
+ \"faabar",
+ \ ]
+let g:test_data_aff9 = [
+ \ ]
+let g:test_data_dic9 = [
+ \"1234",
+ \"foo",
+ \"bar",
+ \ ]
+let g:test_data_aff_sal = [
+ \"SET ISO8859-1",
+ \"TRY esianrtolcdugmphbyfvkwjkqxz-\xEB\xE9\xE8\xEA\xEF\xEE\xE4\xE0\xE2\xF6\xFC\xFB'ESIANRTOLCDUGMPHBYFVKWJKQXZ",
+ \"",
+ \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF",
+ \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF",
+ \"",
+ \"MIDWORD\t'-",
+ \"",
+ \"KEP =",
+ \"RAR ?",
+ \"BAD !",
+ \"",
+ \"PFX I N 1",
+ \"PFX I 0 in .",
+ \"",
+ \"PFX O Y 1",
+ \"PFX O 0 out .",
+ \"",
+ \"SFX S Y 2",
+ \"SFX S 0 s [^s]",
+ \"SFX S 0 es s",
+ \"",
+ \"SFX N N 3",
+ \"SFX N 0 en [^n]",
+ \"SFX N 0 nen n",
+ \"SFX N 0 n .",
+ \"",
+ \"REP 3",
+ \"REP g ch",
+ \"REP ch g",
+ \"REP svp s.v.p.",
+ \"",
+ \"MAP 9",
+ \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5",
+ \"MAP e\xE8\xE9\xEA\xEB",
+ \"MAP i\xEC\xED\xEE\xEF",
+ \"MAP o\xF2\xF3\xF4\xF5\xF6",
+ \"MAP u\xF9\xFA\xFB\xFC",
+ \"MAP n\xF1",
+ \"MAP c\xE7",
+ \"MAP y\xFF\xFD",
+ \"MAP s\xDF",
+ \"",
+ \"SAL AH(AEIOUY)-^ *H",
+ \"SAL AR(AEIOUY)-^ *R",
+ \"SAL A(HR)^ *",
+ \"SAL A^ *",
+ \"SAL AH(AEIOUY)- H",
+ \"SAL AR(AEIOUY)- R",
+ \"SAL A(HR) _",
+ \"SAL \xC0^ *",
+ \"SAL \xC5^ *",
+ \"SAL BB- _",
+ \"SAL B B",
+ \"SAL CQ- _",
+ \"SAL CIA X",
+ \"SAL CH X",
+ \"SAL C(EIY)- S",
+ \"SAL CK K",
+ \"SAL COUGH^ KF",
+ \"SAL CC< C",
+ \"SAL C K",
+ \"SAL DG(EIY) K",
+ \"SAL DD- _",
+ \"SAL D T",
+ \"SAL \xC9< E",
+ \"SAL EH(AEIOUY)-^ *H",
+ \"SAL ER(AEIOUY)-^ *R",
+ \"SAL E(HR)^ *",
+ \"SAL ENOUGH^$ *NF",
+ \"SAL E^ *",
+ \"SAL EH(AEIOUY)- H",
+ \"SAL ER(AEIOUY)- R",
+ \"SAL E(HR) _",
+ \"SAL FF- _",
+ \"SAL F F",
+ \"SAL GN^ N",
+ \"SAL GN$ N",
+ \"SAL GNS$ NS",
+ \"SAL GNED$ N",
+ \"SAL GH(AEIOUY)- K",
+ \"SAL GH _",
+ \"SAL GG9 K",
+ \"SAL G K",
+ \"SAL H H",
+ \"SAL IH(AEIOUY)-^ *H",
+ \"SAL IR(AEIOUY)-^ *R",
+ \"SAL I(HR)^ *",
+ \"SAL I^ *",
+ \"SAL ING6 N",
+ \"SAL IH(AEIOUY)- H",
+ \"SAL IR(AEIOUY)- R",
+ \"SAL I(HR) _",
+ \"SAL J K",
+ \"SAL KN^ N",
+ \"SAL KK- _",
+ \"SAL K K",
+ \"SAL LAUGH^ LF",
+ \"SAL LL- _",
+ \"SAL L L",
+ \"SAL MB$ M",
+ \"SAL MM M",
+ \"SAL M M",
+ \"SAL NN- _",
+ \"SAL N N",
+ \"SAL OH(AEIOUY)-^ *H",
+ \"SAL OR(AEIOUY)-^ *R",
+ \"SAL O(HR)^ *",
+ \"SAL O^ *",
+ \"SAL OH(AEIOUY)- H",
+ \"SAL OR(AEIOUY)- R",
+ \"SAL O(HR) _",
+ \"SAL PH F",
+ \"SAL PN^ N",
+ \"SAL PP- _",
+ \"SAL P P",
+ \"SAL Q K",
+ \"SAL RH^ R",
+ \"SAL ROUGH^ RF",
+ \"SAL RR- _",
+ \"SAL R R",
+ \"SAL SCH(EOU)- SK",
+ \"SAL SC(IEY)- S",
+ \"SAL SH X",
+ \"SAL SI(AO)- X",
+ \"SAL SS- _",
+ \"SAL S S",
+ \"SAL TI(AO)- X",
+ \"SAL TH @",
+ \"SAL TCH-- _",
+ \"SAL TOUGH^ TF",
+ \"SAL TT- _",
+ \"SAL T T",
+ \"SAL UH(AEIOUY)-^ *H",
+ \"SAL UR(AEIOUY)-^ *R",
+ \"SAL U(HR)^ *",
+ \"SAL U^ *",
+ \"SAL UH(AEIOUY)- H",
+ \"SAL UR(AEIOUY)- R",
+ \"SAL U(HR) _",
+ \"SAL V^ W",
+ \"SAL V F",
+ \"SAL WR^ R",
+ \"SAL WH^ W",
+ \"SAL W(AEIOU)- W",
+ \"SAL X^ S",
+ \"SAL X KS",
+ \"SAL Y(AEIOU)- Y",
+ \"SAL ZZ- _",
+ \"SAL Z S",
+ \ ]
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index a3bc04dcd0..f2dfdc7019 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -106,3 +106,11 @@ function! Test_substitute_variants()
endfor
endfor
endfunction
+
+func Test_substitute_repeat()
+ " This caused an invalid memory access.
+ split Xfile
+ s/^/x
+ call feedkeys("Qsc\<CR>y", 'tx')
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim
new file mode 100644
index 0000000000..0446bd9105
--- /dev/null
+++ b/src/nvim/testdir/test_system.vim
@@ -0,0 +1,48 @@
+" Tests for system() and systemlist()
+
+function! Test_System()
+ if !executable('echo') || !executable('cat') || !executable('wc')
+ return
+ endif
+ let out = system('echo 123')
+ " On Windows we may get a trailing space.
+ if out != "123 \n"
+ call assert_equal("123\n", out)
+ endif
+
+ let out = systemlist('echo 123')
+ " On Windows we may get a trailing space and CR.
+ if out != ["123 \r"]
+ call assert_equal(['123'], out)
+ endif
+
+ call assert_equal('123', system('cat', '123'))
+ call assert_equal(['123'], systemlist('cat', '123'))
+ call assert_equal(["as\<NL>df"], systemlist('cat', ["as\<NL>df"]))
+
+ new Xdummy
+ call setline(1, ['asdf', "pw\<NL>er", 'xxxx'])
+ let out = system('wc -l', bufnr('%'))
+ " On OS/X we get leading spaces
+ let out = substitute(out, '^ *', '', '')
+ call assert_equal("3\n", out)
+
+ let out = systemlist('wc -l', bufnr('%'))
+ " On Windows we may get a trailing CR.
+ if out != ["3\r"]
+ " On OS/X we get leading spaces
+ if type(out) == v:t_list
+ let out[0] = substitute(out[0], '^ *', '', '')
+ endif
+ call assert_equal(['3'], out)
+ endif
+
+ let out = systemlist('cat', bufnr('%'))
+ " On Windows we may get a trailing CR.
+ if out != ["asdf\r", "pw\<NL>er\r", "xxxx\r"]
+ call assert_equal(['asdf', "pw\<NL>er", 'xxxx'], out)
+ endif
+ bwipe!
+
+ call assert_fails('call system("wc -l", 99999)', 'E86:')
+endfunction
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index df5b41a64b..2349bd2ae9 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -500,7 +500,7 @@ static void final_column_wrap(UI *ui)
UGrid *grid = &data->grid;
if (grid->col == ui->width) {
grid->col = 0;
- if (grid->row < ui->height) {
+ if (grid->row < MIN(ui->height, grid->height - 1)) {
grid->row++;
}
}
diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c
index 2b5e96ee60..e0880b4c76 100644
--- a/src/nvim/ugrid.c
+++ b/src/nvim/ugrid.c
@@ -135,6 +135,7 @@ static void destroy_cells(UGrid *grid)
xfree(grid->cells[i]);
}
xfree(grid->cells);
+ grid->cells = NULL;
}
}
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index f611a3bb29..b902f82f31 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -2941,17 +2941,20 @@ bool curbufIsChanged(void)
&& (curbuf->b_changed || file_ff_differs(curbuf, true)));
}
-/*
- * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
- * Recursive.
- */
-void u_eval_tree(u_header_T *first_uhp, list_T *list)
+/// Append the list of undo blocks to a newly allocated list
+///
+/// For use in undotree(). Recursive.
+///
+/// @param[in] first_uhp Undo blocks list to start with.
+///
+/// @return [allocated] List with a representation of undo blocks.
+list_T *u_eval_tree(const u_header_T *const first_uhp)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
{
- u_header_T *uhp = first_uhp;
- dict_T *dict;
+ list_T *const list = tv_list_alloc(kListLenMayKnow);
- while (uhp != NULL) {
- dict = tv_dict_alloc();
+ for (const u_header_T *uhp = first_uhp; uhp != NULL; uhp = uhp->uh_prev.ptr) {
+ dict_T *const dict = tv_dict_alloc();
tv_dict_add_nr(dict, S_LEN("seq"), (varnumber_T)uhp->uh_seq);
tv_dict_add_nr(dict, S_LEN("time"), (varnumber_T)uhp->uh_time);
if (uhp == curbuf->b_u_newhead) {
@@ -2965,14 +2968,12 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list)
}
if (uhp->uh_alt_next.ptr != NULL) {
- list_T *alt_list = tv_list_alloc();
-
// Recursive call to add alternate undo tree.
- u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
- tv_dict_add_list(dict, S_LEN("alt"), alt_list);
+ tv_dict_add_list(dict, S_LEN("alt"), u_eval_tree(uhp->uh_alt_next.ptr));
}
tv_list_append_dict(list, dict);
- uhp = uhp->uh_prev.ptr;
}
+
+ return list;
}
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 996c06c902..2181f13c41 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -1281,10 +1281,10 @@ static const int included_patches[] = {
157,
156,
155,
- // 154,
- // 153,
+ 154,
+ 153,
152,
- // 151,
+ 151,
150,
149,
148,
diff --git a/src/nvim/window.c b/src/nvim/window.c
index b687781dfb..4dfc72f212 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -5921,13 +5921,15 @@ void win_get_tabwin(handle_T id, int *tabnr, int *winnr)
}
}
-void win_id2tabwin(typval_T *argvars, list_T *list)
+void win_id2tabwin(typval_T *const argvars, typval_T *const rettv)
{
int winnr = 1;
int tabnr = 1;
handle_T id = (handle_T)tv_get_number(&argvars[0]);
win_get_tabwin(id, &tabnr, &winnr);
+
+ list_T *const list = tv_list_alloc_ret(rettv, 2);
tv_list_append_number(list, tabnr);
tv_list_append_number(list, winnr);
}
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index ff28e3d133..39db831fe3 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -37,7 +37,7 @@ describe('api', function()
os.remove(fname)
end)
- it("VimL error: fails (VimL error), does NOT update v:errmsg", function()
+ it("parse error: fails (specific error), does NOT update v:errmsg", function()
-- Most API methods return generic errors (or no error) if a VimL
-- expression fails; nvim_command returns the VimL error details.
local status, rv = pcall(nvim, "command", "bogus_command")
@@ -45,6 +45,82 @@ describe('api', function()
eq("E492:", string.match(rv, "E%d*:")) -- VimL error was returned.
eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
end)
+
+ it("runtime error: fails (specific error)", function()
+ local status, rv = pcall(nvim, "command_output", "buffer 23487")
+ eq(false, status) -- nvim_command() failed.
+ eq("E86: Buffer 23487 does not exist", string.match(rv, "E%d*:.*"))
+ eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
+ end)
+ end)
+
+ describe('nvim_command_output', function()
+ it('does not induce hit-enter prompt', function()
+ -- Induce a hit-enter prompt use nvim_input (non-blocking).
+ nvim('command', 'set cmdheight=1')
+ nvim('input', [[:echo "hi\nhi2"<CR>]])
+
+ -- Verify hit-enter prompt.
+ eq({mode='r', blocking=true}, nvim("get_mode"))
+ nvim('input', [[<C-c>]])
+
+ -- Verify NO hit-enter prompt.
+ nvim('command_output', [[echo "hi\nhi2"]])
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ end)
+
+ it('captures command output', function()
+ eq('this is\nspinal tap',
+ nvim('command_output', [[echo "this is\nspinal tap"]]))
+ end)
+
+ it('captures empty command output', function()
+ eq('', nvim('command_output', 'echo'))
+ end)
+
+ it('captures single-char command output', function()
+ eq('x', nvim('command_output', 'echo "x"'))
+ end)
+
+ it('captures multiple commands', function()
+ eq('foo\n 1 %a "[No Name]" line 1',
+ nvim('command_output', 'echo "foo" | ls'))
+ end)
+
+ it('captures nested execute()', function()
+ eq('\nnested1\nnested2\n 1 %a "[No Name]" line 1',
+ nvim('command_output',
+ [[echo execute('echo "nested1\nnested2"') | ls]]))
+ end)
+
+ it('captures nested nvim_command_output()', function()
+ eq('nested1\nnested2\n 1 %a "[No Name]" line 1',
+ nvim('command_output',
+ [[echo nvim_command_output('echo "nested1\nnested2"') | ls]]))
+ end)
+
+ it('does not return shell |:!| output', function()
+ eq(':!echo "foo"\r\n', nvim('command_output', [[!echo "foo"]]))
+ end)
+
+ it("parse error: fails (specific error), does NOT update v:errmsg", function()
+ local status, rv = pcall(nvim, "command_output", "bogus commannnd")
+ eq(false, status) -- nvim_command_output() failed.
+ eq("E492: Not an editor command: bogus commannnd",
+ string.match(rv, "E%d*:.*"))
+ eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
+ -- Verify NO hit-enter prompt.
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ end)
+
+ it("runtime error: fails (specific error)", function()
+ local status, rv = pcall(nvim, "command_output", "buffer 42")
+ eq(false, status) -- nvim_command_output() failed.
+ eq("E86: Buffer 42 does not exist", string.match(rv, "E%d*:.*"))
+ eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
+ -- Verify NO hit-enter prompt.
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ end)
end)
describe('nvim_eval', function()
diff --git a/test/functional/autocmd/tabclose_spec.lua b/test/functional/autocmd/tabclose_spec.lua
index fb777e7eea..b7c33dc3d8 100644
--- a/test/functional/autocmd/tabclose_spec.lua
+++ b/test/functional/autocmd/tabclose_spec.lua
@@ -11,10 +11,10 @@ describe('TabClosed', function()
repeat
nvim('command', 'tabnew')
until nvim('eval', 'tabpagenr()') == 6 -- current tab is now 6
- eq("\ntabclosed:6:6:5", nvim('command_output', 'tabclose')) -- close last 6, current tab is now 5
- eq("\ntabclosed:5:5:4", nvim('command_output', 'close')) -- close last window on tab, closes tab
- eq("\ntabclosed:2:2:3", nvim('command_output', '2tabclose')) -- close tab 2, current tab is now 3
- eq("\ntabclosed:1:1:2\ntabclosed:1:1:1", nvim('command_output', 'tabonly')) -- close tabs 1 and 2
+ eq("tabclosed:6:6:5", nvim('command_output', 'tabclose')) -- close last 6, current tab is now 5
+ eq("tabclosed:5:5:4", nvim('command_output', 'close')) -- close last window on tab, closes tab
+ eq("tabclosed:2:2:3", nvim('command_output', '2tabclose')) -- close tab 2, current tab is now 3
+ eq("tabclosed:1:1:2\ntabclosed:1:1:1", nvim('command_output', 'tabonly')) -- close tabs 1 and 2
end)
it('is triggered when closing a window via bdelete from another tab', function()
@@ -23,7 +23,7 @@ describe('TabClosed', function()
nvim('command', '1tabedit Xtestfile')
nvim('command', 'normal! 1gt')
eq({1, 3}, nvim('eval', '[tabpagenr(), tabpagenr("$")]'))
- eq("\ntabclosed:2:2:1\ntabclosed:2:2:1", nvim('command_output', 'bdelete Xtestfile'))
+ eq("tabclosed:2:2:1\ntabclosed:2:2:1", nvim('command_output', 'bdelete Xtestfile'))
eq({1, 1}, nvim('eval', '[tabpagenr(), tabpagenr("$")]'))
end)
@@ -35,7 +35,7 @@ describe('TabClosed', function()
-- Only one tab is closed, and the alternate file is used for the other.
eq({2, 3}, nvim('eval', '[tabpagenr(), tabpagenr("$")]'))
- eq("\ntabclosed:2:2:2", nvim('command_output', 'bdelete Xtestfile2'))
+ eq("tabclosed:2:2:2", nvim('command_output', 'bdelete Xtestfile2'))
eq('Xtestfile1', nvim('eval', 'bufname("")'))
end)
end)
@@ -48,9 +48,9 @@ describe('TabClosed', function()
nvim('command', 'tabnew')
until nvim('eval', 'tabpagenr()') == 7 -- current tab is now 7
-- sanity check, we shouldn't match on tabs with numbers other than 2
- eq("\ntabclosed:7:7:6", nvim('command_output', 'tabclose'))
+ eq("tabclosed:7:7:6", nvim('command_output', 'tabclose'))
-- close tab page 2, current tab is now 5
- eq("\ntabclosed:2:2:5\ntabclosed:match", nvim('command_output', '2tabclose'))
+ eq("tabclosed:2:2:5\ntabclosed:match", nvim('command_output', '2tabclose'))
end)
end)
@@ -59,7 +59,7 @@ describe('TabClosed', function()
nvim('command', 'au! TabClosed * echom "tabclosed:".expand("<afile>").":".expand("<amatch>").":".tabpagenr()')
nvim('command', 'tabedit Xtestfile')
eq({2, 2}, nvim('eval', '[tabpagenr(), tabpagenr("$")]'))
- eq("\ntabclosed:2:2:1", nvim('command_output', 'close'))
+ eq("tabclosed:2:2:1", nvim('command_output', 'close'))
eq({1, 1}, nvim('eval', '[tabpagenr(), tabpagenr("$")]'))
end)
end)
diff --git a/test/functional/autocmd/tabnewentered_spec.lua b/test/functional/autocmd/tabnewentered_spec.lua
index bdbe677132..59cac07b34 100644
--- a/test/functional/autocmd/tabnewentered_spec.lua
+++ b/test/functional/autocmd/tabnewentered_spec.lua
@@ -7,14 +7,14 @@ describe('TabNewEntered', function()
it('matches when entering any new tab', function()
clear()
nvim('command', 'au! TabNewEntered * echom "tabnewentered:".tabpagenr().":".bufnr("")')
- eq("\ntabnewentered:2:2", nvim('command_output', 'tabnew'))
- eq("\n\"test.x2\" [New File]\ntabnewentered:3:3", nvim('command_output', 'tabnew test.x2'))
+ eq("tabnewentered:2:2", nvim('command_output', 'tabnew'))
+ eq("tabnewentered:3:3", nvim('command_output', 'tabnew test.x2'))
end)
end)
describe('with FILE as <afile>', function()
it('matches when opening a new tab for FILE', function()
nvim('command', 'au! TabNewEntered Xtest-tabnewentered echom "tabnewentered:match"')
- eq('\n"Xtest-tabnewentered" [New File]\ntabnewentered:4:4\ntabnewentered:match',
+ eq('tabnewentered:4:4\ntabnewentered:match',
nvim('command_output', 'tabnew Xtest-tabnewentered'))
end)
end)
@@ -24,7 +24,7 @@ describe('TabNewEntered', function()
nvim('command', 'au! TabNewEntered * echom "entered"')
nvim('command', 'tabnew test.x2')
nvim('command', 'split')
- eq('\nentered', nvim('command_output', 'execute "normal \\<C-W>T"'))
+ eq('entered', nvim('command_output', 'execute "normal \\<C-W>T"'))
end)
end)
end)
diff --git a/test/functional/eval/getline_spec.lua b/test/functional/eval/getline_spec.lua
new file mode 100644
index 0000000000..3c56bde094
--- /dev/null
+++ b/test/functional/eval/getline_spec.lua
@@ -0,0 +1,39 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local call = helpers.call
+local clear = helpers.clear
+local eq = helpers.eq
+local expect = helpers.expect
+
+describe('getline()', function()
+ before_each(function()
+ clear()
+ call('setline', 1, {'a', 'b', 'c'})
+ expect([[
+ a
+ b
+ c]])
+ end)
+
+ it('returns empty string for invalid line', function()
+ eq('', call('getline', -1))
+ eq('', call('getline', 0))
+ eq('', call('getline', 4))
+ end)
+
+ it('returns empty list for invalid range', function()
+ eq({}, call('getline', 2, 1))
+ eq({}, call('getline', -1, 1))
+ eq({}, call('getline', 4, 4))
+ end)
+
+ it('returns value of valid line', function()
+ eq('b', call('getline', 2))
+ eq('a', call('getline', '.'))
+ end)
+
+ it('returns value of valid range', function()
+ eq({'a', 'b'}, call('getline', 1, 2))
+ eq({'a', 'b', 'c'}, call('getline', 1, 4))
+ end)
+end)
diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua
index e2b12b6bc9..4d1630042b 100644
--- a/test/functional/eval/system_spec.lua
+++ b/test/functional/eval/system_spec.lua
@@ -5,6 +5,7 @@ local eq, call, clear, eval, feed_command, feed, nvim =
helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command,
helpers.feed, helpers.nvim
local command = helpers.command
+local exc_exec = helpers.exc_exec
local iswin = helpers.iswin
local Screen = require('test.functional.ui.screen')
@@ -274,9 +275,12 @@ describe('system()', function()
end)
end)
- describe('input passed as Number', function()
- it('stringifies the input', function()
- eq('1', eval('system("cat", 1)'))
+ describe('Number input', function()
+ it('is treated as a buffer id', function()
+ command("put ='text in buffer 1'")
+ eq('\ntext in buffer 1\n', eval('system("cat", 1)'))
+ eq('Vim(echo):E86: Buffer 42 does not exist',
+ exc_exec('echo system("cat", 42)'))
end)
end)
diff --git a/test/functional/ex_cmds/map_spec.lua b/test/functional/ex_cmds/map_spec.lua
index b46f83405e..84d5bc2335 100644
--- a/test/functional/ex_cmds/map_spec.lua
+++ b/test/functional/ex_cmds/map_spec.lua
@@ -5,6 +5,7 @@ local feed = helpers.feed
local meths = helpers.meths
local clear = helpers.clear
local command = helpers.command
+local expect = helpers.expect
describe(':*map', function()
before_each(clear)
@@ -18,4 +19,10 @@ describe(':*map', function()
feed('\24\25<C-x><C-y>')
eq(4, meths.get_var('counter'))
end)
+
+ it(':imap <M-">', function()
+ command('imap <M-"> foo')
+ feed('i-<M-">-')
+ expect('-foo-')
+ end)
end)
diff --git a/test/functional/ex_cmds/sign_spec.lua b/test/functional/ex_cmds/sign_spec.lua
index b37e6e8563..df0f5db860 100644
--- a/test/functional/ex_cmds/sign_spec.lua
+++ b/test/functional/ex_cmds/sign_spec.lua
@@ -16,8 +16,8 @@ describe('sign', function()
nvim('command', 'sign place 34 line=3 name=Foo buffer='..buf2)
-- now unplace without specifying a buffer
nvim('command', 'sign unplace 34')
- eq("\n--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf1))
- eq("\n--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf2))
+ eq("--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf1))
+ eq("--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf2))
end)
end)
end)
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 31a2c3b3ff..dfc4694272 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -644,7 +644,7 @@ local function alter_slashes(obj)
end
local function hexdump(str)
- local len = string.len( str )
+ local len = string.len(str)
local dump = ""
local hex = ""
local asc = ""
@@ -652,22 +652,20 @@ local function hexdump(str)
for i = 1, len do
if 1 == i % 8 then
dump = dump .. hex .. asc .. "\n"
- hex = string.format( "%04x: ", i - 1 )
+ hex = string.format("%04x: ", i - 1)
asc = ""
end
- local ord = string.byte( str, i )
- hex = hex .. string.format( "%02x ", ord )
+ local ord = string.byte(str, i)
+ hex = hex .. string.format("%02x ", ord)
if ord >= 32 and ord <= 126 then
- asc = asc .. string.char( ord )
+ asc = asc .. string.char(ord)
else
asc = asc .. "."
end
end
- return dump .. hex
- .. string.rep( " ", 8 - len % 8 ) .. asc
-
+ return dump .. hex .. string.rep(" ", 8 - len % 8) .. asc
end
local module = {
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index 745bfeecf9..a62c03b70f 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -6,15 +6,18 @@ local helpers = require('test.functional.helpers')(after_each)
local thelpers = require('test.functional.terminal.helpers')
local Screen = require('test.functional.ui.screen')
local eq = helpers.eq
-local feed_data = thelpers.feed_data
local feed_command = helpers.feed_command
+local feed_data = thelpers.feed_data
local clear = helpers.clear
+local command = helpers.command
+local eval = helpers.eval
local nvim_dir = helpers.nvim_dir
local retry = helpers.retry
local nvim_prog = helpers.nvim_prog
local nvim_set = helpers.nvim_set
local ok = helpers.ok
local read_file = helpers.read_file
+local wait = helpers.wait
if helpers.pending_win32(pending) then return end
@@ -40,6 +43,28 @@ describe('tui', function()
screen:detach()
end)
+ it('rapid resize #7572 #7628', function()
+ -- Need buffer rows to provoke the behavior.
+ feed_data(":edit test/functional/fixtures/bigfile.txt:")
+ command('call jobresize(b:terminal_job_id, 58, 9)')
+ command('call jobresize(b:terminal_job_id, 62, 13)')
+ command('call jobresize(b:terminal_job_id, 100, 42)')
+ command('call jobresize(b:terminal_job_id, 37, 1000)')
+ -- Resize to <5 columns.
+ screen:try_resize(4, 44)
+ command('call jobresize(b:terminal_job_id, 4, 1000)')
+ -- Resize to 1 row, then to 1 column, then increase rows to 4.
+ screen:try_resize(44, 1)
+ command('call jobresize(b:terminal_job_id, 44, 1)')
+ screen:try_resize(1, 1)
+ command('call jobresize(b:terminal_job_id, 1, 1)')
+ screen:try_resize(1, 4)
+ command('call jobresize(b:terminal_job_id, 1, 4)')
+ screen:try_resize(57, 17)
+ command('call jobresize(b:terminal_job_id, 57, 17)')
+ eq(2, eval("1+1")) -- Still alive?
+ end)
+
it('accepts basic utf-8 input', function()
feed_data('iabc\ntest1\ntest2')
screen:expect([[
@@ -448,7 +473,7 @@ describe("tui 't_Co' (terminal colors)", function()
nvim_set))
feed_data(":echo &t_Co\n")
- helpers.wait()
+ wait()
local tline
if maxcolors == 8 or maxcolors == 16 then
tline = "~ "
diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua
index 73fe94c056..ffb6a26aef 100644
--- a/test/functional/ui/cmdline_highlight_spec.lua
+++ b/test/functional/ui/cmdline_highlight_spec.lua
@@ -849,7 +849,7 @@ describe('Ex commands coloring support', function()
{EOB:~ }|
|
]])
- eq('\nError detected while processing :\nE605: Exception not caught: 42',
+ eq('Error detected while processing :\nE605: Exception not caught: 42',
meths.command_output('messages'))
end)
it('errors out when failing to get callback', function()
diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua
index 6babd4be77..3d1c42c3a0 100644
--- a/test/unit/eval/helpers.lua
+++ b/test/unit/eval/helpers.lua
@@ -312,7 +312,7 @@ local lua2typvalt_type_tab = {
processed[l].lv_refcount = processed[l].lv_refcount + 1
return typvalt(eval.VAR_LIST, {v_list=processed[l]})
end
- local lst = populate_list(eval.tv_list_alloc(), l, processed)
+ local lst = populate_list(eval.tv_list_alloc(#l), l, processed)
return typvalt(eval.VAR_LIST, {v_list=lst})
end,
[dict_type] = function(l, processed)
@@ -433,7 +433,8 @@ local function int(n)
end
local function list(...)
- return populate_list(ffi.gc(eval.tv_list_alloc(), eval.tv_list_unref),
+ return populate_list(ffi.gc(eval.tv_list_alloc(select('#', ...)),
+ eval.tv_list_unref),
{...}, {})
end
diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua
index b668144175..919a42fbb9 100644
--- a/test/unit/eval/typval_spec.lua
+++ b/test/unit/eval/typval_spec.lua
@@ -2407,7 +2407,7 @@ describe('typval.c', function()
describe('list ret()', function()
itp('works', function()
local rettv = typvalt(lib.VAR_UNKNOWN)
- local l = lib.tv_list_alloc_ret(rettv)
+ local l = lib.tv_list_alloc_ret(rettv, 0)
eq(empty_list, typvalt2lua(rettv))
eq(rettv.vval.v_list, l)
end)
diff --git a/test/unit/keymap_spec.lua b/test/unit/keymap_spec.lua
new file mode 100644
index 0000000000..595a19eb17
--- /dev/null
+++ b/test/unit/keymap_spec.lua
@@ -0,0 +1,71 @@
+local helpers = require("test.unit.helpers")(after_each)
+local itp = helpers.gen_itp(it)
+
+local ffi = helpers.ffi
+local eq = helpers.eq
+local neq = helpers.neq
+
+local keymap = helpers.cimport("./src/nvim/keymap.h")
+
+describe('keymap.c', function()
+
+ describe('find_special_key()', function()
+ local srcp = ffi.new('const unsigned char *[1]')
+ local modp = ffi.new('int[1]')
+
+ itp('no keycode', function()
+ srcp[0] = 'abc'
+ eq(0, keymap.find_special_key(srcp, 3, modp, false, false, false))
+ end)
+
+ itp('keycode with multiple modifiers', function()
+ srcp[0] = '<C-M-S-A>'
+ neq(0, keymap.find_special_key(srcp, 9, modp, false, false, false))
+ neq(0, modp[0])
+ end)
+
+ itp('case-insensitive', function()
+ -- Compare other capitalizations to this.
+ srcp[0] = '<C-A>'
+ local all_caps_key =
+ keymap.find_special_key(srcp, 5, modp, false, false, false)
+ local all_caps_mod = modp[0]
+
+ srcp[0] = '<C-a>'
+ eq(all_caps_key,
+ keymap.find_special_key(srcp, 5, modp, false, false, false))
+ eq(all_caps_mod, modp[0])
+
+ srcp[0] = '<c-A>'
+ eq(all_caps_key,
+ keymap.find_special_key(srcp, 5, modp, false, false, false))
+ eq(all_caps_mod, modp[0])
+
+ srcp[0] = '<c-a>'
+ eq(all_caps_key,
+ keymap.find_special_key(srcp, 5, modp, false, false, false))
+ eq(all_caps_mod, modp[0])
+ end)
+
+ itp('double-quote in keycode #7411', function()
+ -- Unescaped with in_string=false
+ srcp[0] = '<C-">'
+ eq(string.byte('"'),
+ keymap.find_special_key(srcp, 5, modp, false, false, false))
+
+ -- Unescaped with in_string=true
+ eq(0, keymap.find_special_key(srcp, 5, modp, false, false, true))
+
+ -- Escaped with in_string=false
+ srcp[0] = '<C-\\">'
+ -- Should fail because the key is invalid
+ -- (more than 1 non-modifier character).
+ eq(0, keymap.find_special_key(srcp, 6, modp, false, false, false))
+
+ -- Escaped with in_string=true
+ eq(string.byte('"'),
+ keymap.find_special_key(srcp, 6, modp, false, false, true))
+ end)
+ end)
+
+end)