aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/common/test.sh1
-rw-r--r--CMakeLists.txt10
-rw-r--r--Makefile8
-rw-r--r--cmake/RunTests.cmake2
-rw-r--r--runtime/autoload/health.vim10
-rw-r--r--runtime/autoload/health/provider.vim60
-rw-r--r--runtime/doc/eval.txt18
-rw-r--r--runtime/doc/tips.txt2
-rw-r--r--runtime/doc/vim_diff.txt6
-rw-r--r--src/nvim/api/private/helpers.c5
-rw-r--r--src/nvim/buffer.c24
-rw-r--r--src/nvim/diff.c16
-rw-r--r--src/nvim/eval.c33
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/eval/typval_encode.c.h25
-rw-r--r--src/nvim/eval/typval_encode.h1
-rw-r--r--src/nvim/eval_defs.h2
-rw-r--r--src/nvim/event/libuv_process.c7
-rw-r--r--src/nvim/event/process.c36
-rw-r--r--src/nvim/globals.h28
-rw-r--r--src/nvim/if_cscope.c5
-rw-r--r--src/nvim/message.c37
-rw-r--r--src/nvim/ops.c19
-rw-r--r--src/nvim/os/env.c32
-rw-r--r--src/nvim/os/fs.c6
-rw-r--r--src/nvim/os/pty_process_unix.c40
-rw-r--r--src/nvim/os/shell.c33
-rw-r--r--src/nvim/po/fr.po13
-rw-r--r--src/nvim/testdir/Makefile24
-rw-r--r--src/nvim/testdir/runtest.vim1
-rw-r--r--src/nvim/testdir/samples/memfile_test.c143
-rw-r--r--src/nvim/testdir/test49.vim2
-rw-r--r--src/nvim/testdir/test_cscope.vim266
-rw-r--r--src/nvim/testdir/test_diffmode.vim16
-rw-r--r--src/nvim/testdir/test_digraph.vim461
-rw-r--r--src/nvim/testdir/test_increment.vim760
-rw-r--r--src/nvim/testdir/test_increment_dbcs.vim29
-rw-r--r--src/nvim/version.c28
-rw-r--r--src/nvim/window.c11
-rw-r--r--test/functional/eval/printf_spec.lua32
-rw-r--r--test/functional/helpers.lua3
41 files changed, 2055 insertions, 201 deletions
diff --git a/.ci/common/test.sh b/.ci/common/test.sh
index dc59f4f793..714dfd297b 100644
--- a/.ci/common/test.sh
+++ b/.ci/common/test.sh
@@ -63,7 +63,6 @@ run_functionaltests() {
}
run_oldtests() {
- ${MAKE_CMD} -C "${BUILD_DIR}" helptags
if ! make -C "${TRAVIS_BUILD_DIR}/src/nvim/testdir"; then
reset
asan_check "${LOG_DIR}"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7fdbad8fca..5d2d0ec97c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -152,9 +152,9 @@ if(NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion")
endif()
-# gcc 4.0 and better turn on _FORTIFY_SOURCE=2 automatically. This currently
+# gcc 4.0+ sets _FORTIFY_SOURCE=2 automatically. This currently
# does not work with Neovim due to some uses of dynamically-sized structures.
-# See https://github.com/neovim/neovim/issues/223 for details.
+# https://github.com/neovim/neovim/issues/223
include(CheckCSourceCompiles)
# Include the build type's default flags in the check for _FORTIFY_SOURCE,
@@ -424,7 +424,6 @@ if(NOT BUSTED_OUTPUT_TYPE)
endif()
find_program(LUACHECK_PRG luacheck)
-
find_program(GPERF_PRG gperf)
include(InstallHelpers)
@@ -457,7 +456,7 @@ get_compile_flags(NVIM_VERSION_CFLAGS)
add_subdirectory(test/includes)
add_subdirectory(config)
-add_subdirectory(test/functional/fixtures) # compile pty/shell test programs
+add_subdirectory(test/functional/fixtures) # compile test programs
add_subdirectory(runtime)
# Setup some test-related bits. We do this after going down the tree because we
@@ -522,8 +521,7 @@ if(BUSTED_PRG)
DEPENDS ${UNITTEST_PREREQS}
${TEST_TARGET_ARGS})
else()
- message(WARNING "The Luajit ffi is not available in ${LUA_PRG}"
- ", disabling unit tests")
+ message(WARNING "disabling unit tests: no Luajit FFI in ${LUA_PRG}")
endif()
add_custom_target(functionaltest
diff --git a/Makefile b/Makefile
index 4696131cc2..2871d007d3 100644
--- a/Makefile
+++ b/Makefile
@@ -85,8 +85,14 @@ endif
mkdir -p build
touch $@
+# TODO: cmake 3.2+ add_custom_target() has a USES_TERMINAL flag.
oldtest: | nvim helptags
- +$(SINGLE_MAKE) -C src/nvim/testdir $(MAKEOVERRIDES)
+ +$(SINGLE_MAKE) -C src/nvim/testdir clean
+ifeq ($(strip $(TEST_FILE)),)
+ +$(SINGLE_MAKE) -C src/nvim/testdir NVIM_PRG="$(realpath build/bin/nvim)" $(MAKEOVERRIDES)
+else
+ +$(SINGLE_MAKE) -C src/nvim/testdir NVIM_PRG="$(realpath build/bin/nvim)" NEW_TESTS=$(TEST_FILE) SCRIPTS= $(MAKEOVERRIDES)
+endif
helptags: | nvim
+$(BUILD_CMD) -C build helptags
diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake
index 19aaf85d0f..38e0f35213 100644
--- a/cmake/RunTests.cmake
+++ b/cmake/RunTests.cmake
@@ -4,7 +4,7 @@ set(ENV{XDG_CONFIG_HOME} ${WORKING_DIR}/Xtest_xdg/config)
set(ENV{XDG_DATA_HOME} ${WORKING_DIR}/Xtest_xdg/share)
if(NVIM_PRG)
- set(ENV{NVIM_PROG} "${NVIM_PRG}")
+ set(ENV{NVIM_PRG} "${NVIM_PRG}")
endif()
if(DEFINED ENV{TEST_FILE})
diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim
index 93ca4dfc54..b0791eb19d 100644
--- a/runtime/autoload/health.vim
+++ b/runtime/autoload/health.vim
@@ -9,17 +9,9 @@ function! s:enhance_syntax() abort
\ containedin=markdownCodeBlock,mkdListItemLine
highlight link healthWarning WarningMsg
- syntax keyword healthInfo INFO
- \ containedin=markdownCodeBlock,mkdListItemLine
- highlight link healthInfo ModeMsg
-
syntax keyword healthSuccess SUCCESS
\ containedin=markdownCodeBlock,mkdListItemLine
- highlight link healthSuccess ModeMsg
-
- syntax keyword healthSuggestion SUGGESTIONS
- \ containedin=markdownCodeBlock,mkdListItemLine
- highlight link healthSuggestion String
+ highlight healthSuccess guibg=#5fff00 guifg=#080808 ctermbg=82 ctermfg=232
syntax match healthHelp "|.\{-}|" contains=healthBar
\ containedin=markdownCodeBlock,mkdListItemLine
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
index 61ab957829..57dd508f96 100644
--- a/runtime/autoload/health/provider.vim
+++ b/runtime/autoload/health/provider.vim
@@ -221,20 +221,26 @@ endfunction
function! s:check_python(version) abort
call health#report_start('Python ' . a:version . ' provider')
- let python_bin_name = 'python'.(a:version == 2 ? '' : '3')
+ let pyname = 'python'.(a:version == 2 ? '' : '3')
let pyenv = resolve(exepath('pyenv'))
let pyenv_root = exists('$PYENV_ROOT') ? resolve($PYENV_ROOT) : 'n'
let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : ''
- let host_prog_var = python_bin_name.'_host_prog'
+ let host_prog_var = pyname.'_host_prog'
+ let loaded_var = 'g:loaded_'.pyname.'_provider'
let python_bin = ''
let python_multiple = []
+ if exists(loaded_var) && !exists('*provider#'.pyname.'#Call')
+ call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var))
+ return
+ endif
+
if exists('g:'.host_prog_var)
call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var)))
endif
- let [python_bin_name, pythonx_errs] = provider#pythonx#Detect(a:version)
- if empty(python_bin_name)
+ let [pyname, pythonx_errs] = provider#pythonx#Detect(a:version)
+ if empty(pyname)
call health#report_warn('No Python interpreter was found with the neovim '
\ . 'module. Using the first available for diagnostics.')
if !empty(pythonx_errs)
@@ -242,21 +248,21 @@ function! s:check_python(version) abort
endif
endif
- if !empty(python_bin_name)
+ if !empty(pyname)
if exists('g:'.host_prog_var)
- let python_bin = exepath(python_bin_name)
+ let python_bin = exepath(pyname)
endif
- let python_bin_name = fnamemodify(python_bin_name, ':t')
+ let pyname = fnamemodify(pyname, ':t')
endif
if !empty(pythonx_errs)
call health#report_error('Python provider error', pythonx_errs)
endif
- if !empty(python_bin_name) && empty(python_bin) && empty(pythonx_errs)
+ if !empty(pyname) && empty(python_bin) && empty(pythonx_errs)
if !exists('g:'.host_prog_var)
call health#report_info(printf('`g:%s` is not set. Searching for '
- \ . '%s in the environment.', host_prog_var, python_bin_name))
+ \ . '%s in the environment.', host_prog_var, pyname))
endif
if !empty(pyenv)
@@ -269,19 +275,19 @@ function! s:check_python(version) abort
call health#report_ok(printf('pyenv found: "%s"', pyenv))
endif
- let python_bin = s:trim(s:system([pyenv, 'which', python_bin_name], '', 1))
+ let python_bin = s:trim(s:system([pyenv, 'which', pyname], '', 1))
if empty(python_bin)
- call health#report_warn(printf('pyenv could not find %s.', python_bin_name))
+ call health#report_warn(printf('pyenv could not find %s.', pyname))
endif
endif
if empty(python_bin)
- let python_bin = exepath(python_bin_name)
+ let python_bin = exepath(pyname)
if exists('$PATH')
for path in split($PATH, has('win32') ? ';' : ':')
- let path_bin = path.'/'.python_bin_name
+ let path_bin = path.'/'.pyname
if path_bin != python_bin && index(python_multiple, path_bin) == -1
\ && executable(path_bin)
call add(python_multiple, path_bin)
@@ -292,7 +298,7 @@ function! s:check_python(version) abort
" This is worth noting since the user may install something
" that changes $PATH, like homebrew.
call health#report_info(printf('Multiple %s executables found. '
- \ . 'Set `g:%s` to avoid surprises.', python_bin_name, host_prog_var))
+ \ . 'Set `g:%s` to avoid surprises.', pyname, host_prog_var))
endif
if python_bin =~# '\<shims\>'
@@ -333,9 +339,9 @@ function! s:check_python(version) abort
endif
endif
- if empty(python_bin) && !empty(python_bin_name)
+ if empty(python_bin) && !empty(pyname)
" An error message should have already printed.
- call health#report_error(printf('`%s` was not found.', python_bin_name))
+ call health#report_error(printf('`%s` was not found.', pyname))
elseif !empty(python_bin) && !s:check_bin(python_bin)
let python_bin = ''
endif
@@ -349,7 +355,7 @@ function! s:check_python(version) abort
if $VIRTUAL_ENV != pyenv_prefix
let virtualenv_inactive = 1
endif
- elseif !empty(python_bin_name) && exepath(python_bin_name) !~# '^'.$VIRTUAL_ENV.'/'
+ elseif !empty(pyname) && exepath(pyname) !~# '^'.$VIRTUAL_ENV.'/'
let virtualenv_inactive = 1
endif
endif
@@ -381,9 +387,9 @@ function! s:check_python(version) abort
call health#report_info('Python'.a:version.' version: ' . pyversion)
if s:is_bad_response(status)
- call health#report_info(printf('%s-neovim version: %s (%s)', python_bin_name, current, status))
+ call health#report_info(printf('%s-neovim version: %s (%s)', pyname, current, status))
else
- call health#report_info(printf('%s-neovim version: %s', python_bin_name, current))
+ call health#report_info(printf('%s-neovim version: %s', pyname, current))
endif
if s:is_bad_response(current)
@@ -397,10 +403,10 @@ function! s:check_python(version) abort
call health#report_error('HTTP request failed: '.latest)
elseif s:is_bad_response(status)
call health#report_warn(printf('Latest %s-neovim is NOT installed: %s',
- \ python_bin_name, latest))
+ \ pyname, latest))
elseif !s:is_bad_response(current)
call health#report_ok(printf('Latest %s-neovim is installed: %s',
- \ python_bin_name, latest))
+ \ pyname, latest))
endif
endif
@@ -409,6 +415,12 @@ endfunction
function! s:check_ruby() abort
call health#report_start('Ruby provider')
+ let loaded_var = 'g:loaded_ruby_provider'
+ if exists(loaded_var) && !exists('*provider#ruby#Call')
+ call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var))
+ return
+ endif
+
if !executable('ruby') || !executable('gem')
call health#report_warn(
\ "`ruby` and `gem` must be in $PATH.",
@@ -420,7 +432,9 @@ function! s:check_ruby() abort
let host = provider#ruby#Detect()
if empty(host)
call health#report_warn('Missing "neovim" gem.',
- \ ['Run in shell: gem install neovim'])
+ \ ['Run in shell: gem install neovim',
+ \ 'Is the gem bin directory in $PATH? Check `gem environment`.',
+ \ 'If you are using rvm/rbenv/chruby, try "rehashing".'])
return
endif
call health#report_info('Host: '. host)
@@ -449,7 +463,7 @@ function! s:check_ruby() abort
\ current_gem, latest_gem),
\ ['Run in shell: gem update neovim'])
else
- call health#report_ok('Gem "neovim" is up-to-date: '. current_gem)
+ call health#report_ok('Latest "neovim" gem is installed: '. current_gem)
endif
endfunction
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index b729519d93..a14221a656 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2036,6 +2036,7 @@ insert({list}, {item} [, {idx}])
invert({expr}) Number bitwise invert
isdirectory({directory}) Number TRUE if {directory} is a directory
islocked({expr}) Number TRUE if {expr} is locked
+id({expr}) String identifier of the container
items({dict}) List key-value pairs in {dict}
jobclose({job}[, {stream}]) Number Closes a job stream(s)
jobpid({job}) Number Returns pid of a job.
@@ -4629,6 +4630,22 @@ islocked({expr}) *islocked()* *E786*
< When {expr} is a variable that does not exist you get an error
message. Use |exists()| to check for existence.
+id({expr}) *id()*
+ Returns a |String| which is a unique identifier of the
+ container type (|List|, |Dict| and |Partial|). It is
+ guaranteed that for the mentioned types `id(v1) ==# id(v2)`
+ returns true iff `type(v1) == type(v2) && v1 is v2` (note:
+ |v:_null_list| and |v:_null_dict| have the same `id()` with
+ different types because they are internally represented as
+ a NULL pointers). Currently `id()` returns a hexadecimal
+ representanion of the pointers to the containers (i.e. like
+ `0x994a40`), same as `printf("%p", {expr})`, but it is advised
+ against counting on exact format of return value.
+
+ It is not guaranteed that `id(no_longer_existing_container)`
+ will not be equal to some other `id()`: new containers may
+ reuse identifiers of the garbage-collected ones.
+
items({dict}) *items()*
Return a |List| with all the key-value pairs of {dict}. Each
|List| item is a list with two items: the key of a {dict}
@@ -5500,6 +5517,7 @@ printf({fmt}, {expr1} ...) *printf()*
%g floating point number, as %f or %e depending on value
%G floating point number, as %f or %E depending on value
%% the % character itself
+ %p representation of the pointer to the container
Conversion specifications start with '%' and end with the
conversion type. All other characters are copied unchanged to
diff --git a/runtime/doc/tips.txt b/runtime/doc/tips.txt
index 8032af7d0a..9d2eb3deba 100644
--- a/runtime/doc/tips.txt
+++ b/runtime/doc/tips.txt
@@ -400,7 +400,7 @@ when they are not in the same location as the compressed "doc" directory. See
==============================================================================
Hex editing *hex-editing* *using-xxd*
-See section |23.4| of the user manual.
+See section |23.3| of the user manual.
If one has a particular extension that one uses for binary files (such as exe,
bin, etc), you may find it helpful to automate the process with the following
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index de93aab399..eeb5e85036 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -232,6 +232,12 @@ Additional differences:
itself.
- ShaDa file keeps search direction (|v:searchforward|), viminfo does not.
+|printf()| returns something meaningful when used with `%p` argument: in Vim
+it used to return useless address of the string (strings are copied to the
+newly allocated memory all over the place) and fail on types which cannot be
+coerced to strings. See |id()| for more details, currently it uses
+`printf("%p", {expr})` internally.
+
==============================================================================
5. Missing legacy features *nvim-features-missing*
*if_lua* *if_perl* *if_mzscheme* *if_tcl*
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 6f3f654bdc..ba4d005e9a 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -380,7 +380,7 @@ static inline void typval_encode_list_start(EncodedData *const edata,
kv_push(edata->stack, ARRAY_OBJ(((Array) {
.capacity = len,
.size = 0,
- .items = xmalloc(len * sizeof(*((Object *)NULL)->data.array.items)),
+ .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.array.items)),
})));
}
@@ -422,7 +422,8 @@ static inline void typval_encode_dict_start(EncodedData *const edata,
kv_push(edata->stack, DICTIONARY_OBJ(((Dictionary) {
.capacity = len,
.size = 0,
- .items = xmalloc(len * sizeof(*((Object *)NULL)->data.dictionary.items)),
+ .items = xmalloc(len * sizeof(
+ *((Object)OBJECT_INIT).data.dictionary.items)),
})));
}
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 1089a2dc3b..a471ebf06f 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -4216,6 +4216,8 @@ do_arg_all (
win_T *new_curwin = NULL;
tabpage_T *new_curtab = NULL;
+ assert(firstwin != NULL); // satisfy coverity
+
if (ARGCOUNT <= 0) {
/* Don't give an error message. We don't want it when the ":all"
* command is in the .vimrc. */
@@ -4407,19 +4409,23 @@ do_arg_all (
/* Remove the "lock" on the argument list. */
alist_unlink(alist);
- --autocmd_no_enter;
- /* restore last referenced tabpage's curwin */
+ autocmd_no_enter--;
+ // restore last referenced tabpage's curwin
if (last_curtab != new_curtab) {
- if (valid_tabpage(last_curtab))
- goto_tabpage_tp(last_curtab, TRUE, TRUE);
- if (win_valid(last_curwin))
+ if (valid_tabpage(last_curtab)) {
+ goto_tabpage_tp(last_curtab, true, true);
+ }
+ if (win_valid(last_curwin)) {
win_enter(last_curwin, false);
+ }
+ }
+ // to window with first arg
+ if (valid_tabpage(new_curtab)) {
+ goto_tabpage_tp(new_curtab, true, true);
}
- /* to window with first arg */
- if (valid_tabpage(new_curtab))
- goto_tabpage_tp(new_curtab, TRUE, TRUE);
- if (win_valid(new_curwin))
+ if (win_valid(new_curwin)) {
win_enter(new_curwin, false);
+ }
--autocmd_no_leave;
xfree(opened);
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index aafd50687e..5940dc55da 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -1007,6 +1007,10 @@ void ex_diffsplit(exarg_T *eap)
bufref_T old_curbuf;
set_bufref(&old_curbuf, curbuf);
+ // Need to compute w_fraction when no redraw happened yet.
+ validate_cursor();
+ set_fraction(curwin);
+
// don't use a new tab page, each tab page has its own diffs
cmdmod.tab = 0;
@@ -1032,6 +1036,9 @@ void ex_diffsplit(exarg_T *eap)
curwin->w_cursor.lnum);
}
}
+ // Now that lines are folded scroll to show the cursor at the same
+ // relative position.
+ scroll_to_fraction(curwin, curwin->w_height);
}
}
}
@@ -1154,10 +1161,13 @@ void ex_diffoff(exarg_T *eap)
}
foldUpdateAll(wp);
-
- // make sure topline is not halfway through a fold
- changed_window_setting_win(wp);
}
+ // remove filler lines
+ wp->w_topfill = 0;
+
+ // make sure topline is not halfway a fold and cursor is
+ // invalidated
+ changed_window_setting_win(wp);
// Note: 'sbo' is not restored, it's a global option.
diff_buf_adjust(wp);
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 1cc84f8c16..bbb6565509 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -498,6 +498,14 @@ static PMap(uint64_t) *jobs = NULL;
static uint64_t last_timer_id = 0;
static PMap(uint64_t) *timers = NULL;
+/// Dummy va_list for passing to vim_snprintf
+///
+/// Used because:
+/// - passing a NULL pointer doesn't work when va_list isn't a pointer
+/// - locally in the function results in a "used before set" warning
+/// - using va_start() to initialize it gives "function with fixed args" error
+static va_list dummy_ap;
+
static const char *const msgpack_type_names[] = {
[kMPNil] = "nil",
[kMPBoolean] = "boolean",
@@ -12122,6 +12130,16 @@ static void dict_list(typval_T *argvars, typval_T *rettv, int what)
}
}
+/// "id()" function
+static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const int len = vim_vsnprintf(NULL, 0, "%p", dummy_ap, argvars);
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xmalloc(len + 1);
+ vim_vsnprintf((char *)rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars);
+}
+
/*
* "items(dict)" function
*/
@@ -13628,12 +13646,6 @@ static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = lnum;
}
-/* This dummy va_list is here because:
- * - passing a NULL pointer doesn't work when va_list isn't a pointer
- * - locally in the function results in a "used before set" warning
- * - using va_start() to initialize it gives "function with fixed args" error */
-static va_list ap;
-
/*
* "printf()" function
*/
@@ -13650,11 +13662,11 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/* Get the required length, allocate the buffer and do it for real. */
did_emsg = FALSE;
fmt = (char *)get_tv_string_buf(&argvars[0], buf);
- len = vim_vsnprintf(NULL, 0, fmt, ap, argvars + 1);
+ len = vim_vsnprintf(NULL, 0, fmt, dummy_ap, argvars + 1);
if (!did_emsg) {
char *s = xmalloc(len + 1);
rettv->vval.v_string = (char_u *)s;
- (void)vim_vsnprintf(s, len + 1, fmt, ap, argvars + 1);
+ (void)vim_vsnprintf(s, len + 1, fmt, dummy_ap, argvars + 1);
}
did_emsg |= saved_did_emsg;
}
@@ -23046,8 +23058,9 @@ static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)
data->refcount++;
char *cmd = xstrdup(proc->argv[0]);
- if (!process_spawn(proc)) {
- EMSG2(_(e_jobspawn), cmd);
+ int status = process_spawn(proc);
+ if (status) {
+ EMSG3(_(e_jobspawn), os_strerror(status), cmd);
xfree(cmd);
if (proc->type == kProcessTypePty) {
xfree(data->proc.pty.term_name);
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index e0b72feb19..964b061e95 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -168,6 +168,7 @@ return {
invert={args=1},
isdirectory={args=1},
islocked={args=1},
+ id={args=1},
items={args=1},
jobclose={args={1, 2}},
jobpid={args=1},
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
index 365eb2dd77..34f88cbc98 100644
--- a/src/nvim/eval/typval_encode.c.h
+++ b/src/nvim/eval/typval_encode.c.h
@@ -348,6 +348,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
_mp_push(*mpstack, ((MPConvStackVal) {
.type = kMPConvPartial,
.tv = tv,
+ .saved_copyID = copyID - 1,
.data = {
.p = {
.stage = kMPConvPartialArgs,
@@ -362,12 +363,15 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_CONV_EMPTY_LIST(tv);
break;
}
+ const int saved_copyID = tv->vval.v_list->lv_copyID;
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
kMPConvList);
TYPVAL_ENCODE_CONV_LIST_START(tv, tv->vval.v_list->lv_len);
+ assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) {
.type = kMPConvList,
.tv = tv,
+ .saved_copyID = saved_copyID,
.data = {
.l = {
.list = tv->vval.v_list,
@@ -495,14 +499,17 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
if (val_di->di_tv.v_type != VAR_LIST) {
goto _convert_one_value_regular_dict;
}
+ const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID;
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
lv_copyID, copyID,
kMPConvList);
TYPVAL_ENCODE_CONV_LIST_START(tv,
val_di->di_tv.vval.v_list->lv_len);
+ assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
.type = kMPConvList,
+ .saved_copyID = saved_copyID,
.data = {
.l = {
.list = val_di->di_tv.vval.v_list,
@@ -528,13 +535,16 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
goto _convert_one_value_regular_dict;
}
}
+ const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID;
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
kMPConvPairs);
TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR,
val_list->lv_len);
+ assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
.type = kMPConvPairs,
+ .saved_copyID = saved_copyID,
.data = {
.l = {
.list = val_list,
@@ -569,14 +579,17 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
}
break;
}
-_convert_one_value_regular_dict:
+_convert_one_value_regular_dict: {}
+ const int saved_copyID = tv->vval.v_dict->dv_copyID;
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, copyID,
kMPConvDict);
TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict,
tv->vval.v_dict->dv_hashtab.ht_used);
+ assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(*mpstack, ((MPConvStackVal) {
.tv = tv,
.type = kMPConvDict,
+ .saved_copyID = saved_copyID,
.data = {
.d = {
.dict = tv->vval.v_dict,
@@ -638,7 +651,7 @@ typval_encode_stop_converting_one_item:
case kMPConvDict: {
if (!cur_mpsv->data.d.todo) {
(void)_mp_pop(mpstack);
- cur_mpsv->data.d.dict->dv_copyID = copyID - 1;
+ cur_mpsv->data.d.dict->dv_copyID = cur_mpsv->saved_copyID;
TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, *cur_mpsv->data.d.dictp);
continue;
} else if (cur_mpsv->data.d.todo
@@ -662,7 +675,7 @@ typval_encode_stop_converting_one_item:
case kMPConvList: {
if (cur_mpsv->data.l.li == NULL) {
(void)_mp_pop(mpstack);
- cur_mpsv->data.l.list->lv_copyID = copyID - 1;
+ cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID;
TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv);
continue;
} else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
@@ -675,7 +688,7 @@ typval_encode_stop_converting_one_item:
case kMPConvPairs: {
if (cur_mpsv->data.l.li == NULL) {
(void)_mp_pop(mpstack);
- cur_mpsv->data.l.list->lv_copyID = copyID - 1;
+ cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID;
TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
continue;
} else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
@@ -711,6 +724,7 @@ typval_encode_stop_converting_one_item:
_mp_push(mpstack, ((MPConvStackVal) {
.type = kMPConvPartialList,
.tv = NULL,
+ .saved_copyID = copyID - 1,
.data = {
.a = {
.arg = pt->pt_argv,
@@ -731,6 +745,7 @@ typval_encode_stop_converting_one_item:
TYPVAL_ENCODE_CONV_EMPTY_DICT(NULL, pt->pt_dict);
continue;
}
+ const int saved_copyID = dict->dv_copyID;
const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
TYPVAL_ENCODE_FIRST_ARG_NAME,
dict, &dict->dv_copyID, &mpstack, copyID, kMPConvDict,
@@ -744,9 +759,11 @@ typval_encode_stop_converting_one_item:
}
TYPVAL_ENCODE_CONV_DICT_START(NULL, pt->pt_dict,
dict->dv_hashtab.ht_used);
+ assert(saved_copyID != copyID && saved_copyID != copyID - 1);
_mp_push(mpstack, ((MPConvStackVal) {
.type = kMPConvDict,
.tv = NULL,
+ .saved_copyID = saved_copyID,
.data = {
.d = {
.dict = dict,
diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h
index ba325b8f55..46145c5d03 100644
--- a/src/nvim/eval/typval_encode.h
+++ b/src/nvim/eval/typval_encode.h
@@ -34,6 +34,7 @@ typedef enum {
typedef struct {
MPConvStackValType type; ///< Type of the stack entry.
typval_T *tv; ///< Currently converted typval_T.
+ int saved_copyID; ///< copyID item used to have.
union {
struct {
dict_T *dict; ///< Currently converted dictionary.
diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h
index f478d19ca1..616c89671b 100644
--- a/src/nvim/eval_defs.h
+++ b/src/nvim/eval_defs.h
@@ -49,7 +49,7 @@ typedef enum {
typedef struct {
VarType v_type; ///< Variable type.
VarLockStatus v_lock; ///< Variable lock status.
- union {
+ union typval_vval_union {
varnumber_T v_number; ///< Number, for VAR_NUMBER.
SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL.
float_T v_float; ///< Floating-point number, for VAR_FLOAT.
diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c
index 907187aa17..3da0c386b4 100644
--- a/src/nvim/event/libuv_process.c
+++ b/src/nvim/event/libuv_process.c
@@ -13,7 +13,8 @@
# include "event/libuv_process.c.generated.h"
#endif
-bool libuv_process_spawn(LibuvProcess *uvproc)
+/// @returns zero on success, or negative error code
+int libuv_process_spawn(LibuvProcess *uvproc)
FUNC_ATTR_NONNULL_ALL
{
Process *proc = (Process *)uvproc;
@@ -51,11 +52,11 @@ bool libuv_process_spawn(LibuvProcess *uvproc)
int status;
if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) {
ELOG("uv_spawn failed: %s", uv_strerror(status));
- return false;
+ return status;
}
proc->pid = uvproc->uv.pid;
- return true;
+ return status;
}
void libuv_process_close(LibuvProcess *uvproc)
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index dc7886469b..4429a65f92 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -30,7 +30,8 @@
static bool process_is_tearing_down = false;
-bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
+/// @returns zero on success, or negative error code
+int process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
{
if (proc->in) {
uv_pipe_init(&proc->loop->uv, &proc->in->uv.pipe, 0);
@@ -44,19 +45,19 @@ bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
uv_pipe_init(&proc->loop->uv, &proc->err->uv.pipe, 0);
}
- bool success;
+ int status;
switch (proc->type) {
case kProcessTypeUv:
- success = libuv_process_spawn((LibuvProcess *)proc);
+ status = libuv_process_spawn((LibuvProcess *)proc);
break;
case kProcessTypePty:
- success = pty_process_spawn((PtyProcess *)proc);
+ status = pty_process_spawn((PtyProcess *)proc);
break;
default:
abort();
}
- if (!success) {
+ if (status) {
if (proc->in) {
uv_close((uv_handle_t *)&proc->in->uv.pipe, NULL);
}
@@ -74,7 +75,7 @@ bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
}
shell_free_argv(proc->argv);
proc->status = -1;
- return false;
+ return status;
}
if (proc->in) {
@@ -105,7 +106,7 @@ bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL
proc->internal_close_cb = decref;
proc->refcount++;
kl_push(WatcherPtr, proc->loop->children, proc);
- return true;
+ return 0;
}
void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL
@@ -155,19 +156,16 @@ void process_close_err(Process *proc) FUNC_ATTR_NONNULL_ALL
/// Synchronously wait for a process to finish
///
-/// @param process The Process instance
-/// @param ms Number of milliseconds to wait, 0 for not waiting, -1 for
-/// waiting until the process quits.
-/// @return returns the status code of the exited process. -1 if the process is
-/// still running and the `timeout` has expired. Note that this is
-/// indistinguishable from the process returning -1 by itself. Which
-/// is possible on some OS. Returns -2 if an user has interruped the
-/// wait.
+/// @param process Process instance
+/// @param ms Time in milliseconds to wait for the process.
+/// 0 for no wait. -1 to wait until the process quits.
+/// @return Exit code of the process.
+/// -1 if the timeout expired while the process is still running.
+/// -2 if the user interruped the wait.
int process_wait(Process *proc, int ms, MultiQueue *events)
FUNC_ATTR_NONNULL_ARG(1)
{
- // The default status is -1, which represents a timeout
- int status = -1;
+ int status = -1; // default
bool interrupted = false;
if (!proc->refcount) {
status = proc->status;
@@ -179,8 +177,8 @@ int process_wait(Process *proc, int ms, MultiQueue *events)
events = proc->events;
}
- // Increase refcount to stop the exit callback from being called(and possibly
- // being freed) before we have a chance to get the status.
+ // Increase refcount to stop the exit callback from being called (and possibly
+ // freed) before we have a chance to get the status.
proc->refcount++;
LOOP_PROCESS_EVENTS_UNTIL(proc->loop, events, ms,
// Until...
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 9da6062ffb..20a00e1d9c 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -13,10 +13,6 @@
#include "nvim/types.h"
#include "nvim/event/loop.h"
-/*
- * definition of global variables
- */
-
#define IOSIZE (1024+1) // file I/O and sprintf buffer size
#define MAX_MCO 6 // maximum value for 'maxcombine'
@@ -25,11 +21,8 @@
# define MSG_BUF_CLEN (MSG_BUF_LEN / 6) // cell length (worst case: utf-8
// takes 6 bytes for one cell)
-/*
- * Maximum length of a path (for non-unix systems) Make it a bit long, to stay
- * on the safe side. But not too long to put on the stack.
- * TODO(metrix78): Move this to os_defs.h
- */
+// Maximum length of a file path. Make it a bit long, to stay
+// on the safe side. But not too long to put on the stack.
#ifndef MAXPATHL
# ifdef MAXPATHLEN
# define MAXPATHL MAXPATHLEN
@@ -108,12 +101,9 @@ typedef enum {
* They may have different values when the screen wasn't (re)allocated yet
* after setting Rows or Columns (e.g., when starting up).
*/
-
-#define DFLT_COLS 80 /* default value for 'columns' */
-#define DFLT_ROWS 24 /* default value for 'lines' */
-
+#define DFLT_COLS 80 // default value for 'columns'
+#define DFLT_ROWS 24 // default value for 'lines'
EXTERN long Rows INIT(= DFLT_ROWS); // nr of rows in the screen
-
EXTERN long Columns INIT(= DFLT_COLS); // nr of columns in the screen
/*
@@ -889,9 +879,11 @@ EXTERN int swap_exists_action INIT(= SEA_NONE);
EXTERN int swap_exists_did_quit INIT(= FALSE);
/* Selected "quit" at the dialog. */
-EXTERN char_u IObuff[IOSIZE]; /* sprintf's are done in this buffer */
-EXTERN char_u NameBuff[MAXPATHL]; /* buffer for expanding file names */
-EXTERN char_u msg_buf[MSG_BUF_LEN]; /* small buffer for messages */
+EXTERN char_u IObuff[IOSIZE]; ///< Buffer for sprintf, I/O, etc.
+EXTERN char_u NameBuff[MAXPATHL]; ///< Buffer for expanding file names
+EXTERN char_u msg_buf[MSG_BUF_LEN]; ///< Small buffer for messages
+EXTERN char os_buf[MAX(MAXPATHL, IOSIZE)]; ///< Buffer for the os/ layer
+
/* When non-zero, postpone redrawing. */
EXTERN int RedrawingDisabled INIT(= 0);
@@ -1127,7 +1119,7 @@ EXTERN char_u e_isadir2[] INIT(= N_("E17: \"%s\" is a directory"));
EXTERN char_u e_invjob[] INIT(= N_("E900: Invalid job id"));
EXTERN char_u e_jobtblfull[] INIT(= N_("E901: Job table is full"));
EXTERN char_u e_jobspawn[] INIT(= N_(
- "E903: Process for command \"%s\" could not be spawned"));
+ "E903: Process failed to start: %s: \"%s\""));
EXTERN char_u e_jobnotpty[] INIT(= N_("E904: Job is not connected to a pty"));
EXTERN char_u e_libcall[] INIT(= N_("E364: Library call failed for \"%s()\""));
EXTERN char_u e_mkdir[] INIT(= N_("E739: Cannot create directory %s: %s"));
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index 0b20647771..9d50058257 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -1294,9 +1294,10 @@ static int cs_kill(exarg_T *eap)
}
}
- if (i >= csinfo_size || csinfo[i].fname == NULL) {
- if (p_csverbose)
+ if (!killall && (i >= csinfo_size || csinfo[i].fname == NULL)) {
+ if (p_csverbose) {
(void)EMSG2(_("E261: cscope connection %s not found"), stok);
+ }
return CSCOPE_FAILURE;
} else {
if (killall) {
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 6104adf2c7..ad1c63eeac 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -11,6 +11,7 @@
#include "nvim/vim.h"
#include "nvim/ascii.h"
+#include "nvim/assert.h"
#include "nvim/message.h"
#include "nvim/charset.h"
#include "nvim/eval.h"
@@ -3043,6 +3044,38 @@ static char *tv_str(typval_T *tvs, int *idxp)
return s;
}
+/// Get pointer argument from the next entry in tvs
+///
+/// First entry is 1. Returns NULL for an error.
+///
+/// @param[in] tvs List of typval_T values.
+/// @param[in,out] idxp Pointer to the index of the current value.
+///
+/// @return Pointer stored in typval_T or NULL.
+static const void *tv_ptr(const typval_T *const tvs, int *const idxp)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+#define OFF(attr) offsetof(union typval_vval_union, attr)
+ STATIC_ASSERT(
+ OFF(v_string) == OFF(v_list)
+ && OFF(v_string) == OFF(v_dict)
+ && OFF(v_string) == OFF(v_partial)
+ && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_list)
+ && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_dict)
+ && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_partial),
+ "Strings, dictionaries, lists and partials are expected to be pointers, "
+ "so that all three of them can be accessed via v_string");
+#undef OFF
+ const int idx = *idxp - 1;
+ if (tvs[idx].v_type == VAR_UNKNOWN) {
+ EMSG(_(e_printf));
+ return NULL;
+ } else {
+ (*idxp)++;
+ return tvs[idx].vval.v_string;
+ }
+}
+
/*
* Get float argument from "idxp" entry in "tvs". First entry is 1.
*/
@@ -3369,11 +3402,11 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
size_t size_t_arg = 0;
// only defined for p conversion
- void *ptr_arg = NULL;
+ const void *ptr_arg = NULL;
if (fmt_spec == 'p') {
length_modifier = '\0';
- ptr_arg = tvs ? (void *)tv_str(tvs, &arg_idx) : va_arg(ap, void *);
+ ptr_arg = tvs ? tv_ptr(tvs, &arg_idx) : va_arg(ap, void *);
if (ptr_arg) {
arg_sign = 1;
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 9a01891483..d58c8700ca 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -4455,12 +4455,14 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
if (dobin) {
while (col > 0 && ascii_isbdigit(ptr[col])) {
col--;
+ col -= utf_head_off(ptr, ptr + col);
}
}
if (dohex) {
while (col > 0 && ascii_isxdigit(ptr[col])) {
col--;
+ col -= utf_head_off(ptr, ptr + col);
}
}
if (dobin
@@ -4468,6 +4470,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
&& !((col > 0
&& (ptr[col] == 'X' || ptr[col] == 'x')
&& ptr[col - 1] == '0'
+ && !utf_head_off(ptr, ptr + col - 1)
&& ascii_isxdigit(ptr[col + 1])))) {
// In case of binary/hexadecimal pattern overlap match, rescan
@@ -4475,6 +4478,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
while (col > 0 && ascii_isdigit(ptr[col])) {
col--;
+ col -= utf_head_off(ptr, ptr + col);
}
}
@@ -4482,14 +4486,17 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
&& col > 0
&& (ptr[col] == 'X' || ptr[col] == 'x')
&& ptr[col - 1] == '0'
+ && !utf_head_off(ptr, ptr + col - 1)
&& ascii_isxdigit(ptr[col + 1]))
|| (dobin
&& col > 0
&& (ptr[col] == 'B' || ptr[col] == 'b')
&& ptr[col - 1] == '0'
+ && !utf_head_off(ptr, ptr + col - 1)
&& ascii_isbdigit(ptr[col + 1]))) {
// Found hexadecimal or binary number, move to its start.
col--;
+ col -= utf_head_off(ptr, ptr + col);
} else {
// Search forward and then backward to find the start of number.
col = pos->col;
@@ -4511,15 +4518,18 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
if (visual) {
while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col])
&& !(doalp && ASCII_ISALPHA(ptr[col]))) {
- col++;
- length--;
+ int mb_len = MB_PTR2LEN(ptr + col);
+
+ col += mb_len;
+ length -= mb_len;
}
if (length == 0) {
goto theend;
}
- if (col > pos->col && ptr[col - 1] == '-') {
+ if (col > pos->col && ptr[col - 1] == '-'
+ && !utf_head_off(ptr, ptr + col - 1)) {
negative = true;
was_positive = false;
}
@@ -4565,7 +4575,8 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
endpos = curwin->w_cursor;
curwin->w_cursor.col = col;
} else {
- if (col > 0 && ptr[col - 1] == '-' && !visual) {
+ if (col > 0 && ptr[col - 1] == '-'
+ && !utf_head_off(ptr, ptr + col - 1) && !visual) {
// negative number
col--;
negative = true;
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 5a3c1ef2c8..1697d5edb2 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -164,11 +164,11 @@ static char_u *homedir = NULL;
void init_homedir(void)
{
- // In case we are called a second time (when 'encoding' changes).
+ // In case we are called a second time.
xfree(homedir);
homedir = NULL;
- char_u *var = (char_u *)os_getenv("HOME");
+ const char *var = os_getenv("HOME");
#ifdef WIN32
// Typically, $HOME is not defined on Windows, unless the user has
@@ -182,10 +182,10 @@ void init_homedir(void)
homepath = "\\";
}
if (homedrive != NULL && strlen(homedrive) + strlen(homepath) < MAXPATHL) {
- snprintf((char *)NameBuff, MAXPATHL, "%s%s", homedrive, homepath);
- if (NameBuff[0] != NUL) {
- var = NameBuff;
- vim_setenv("HOME", (char *)NameBuff);
+ snprintf(os_buf, MAXPATHL, "%s%s", homedrive, homepath);
+ if (os_buf[0] != NUL) {
+ var = os_buf;
+ vim_setenv("HOME", os_buf);
}
}
}
@@ -195,17 +195,16 @@ void init_homedir(void)
#ifdef UNIX
// Change to the directory and get the actual path. This resolves
// links. Don't do it when we can't return.
- if (os_dirname(NameBuff, MAXPATHL) == OK
- && os_chdir((char *)NameBuff) == 0) {
- if (!os_chdir((char *)var) && os_dirname(IObuff, IOSIZE) == OK) {
- var = IObuff;
+ if (os_dirname((char_u *)os_buf, MAXPATHL) == OK && os_chdir(os_buf) == 0) {
+ if (!os_chdir(var) && os_dirname(IObuff, IOSIZE) == OK) {
+ var = (char *)IObuff;
}
- if (os_chdir((char *)NameBuff) != 0) {
+ if (os_chdir(os_buf) != 0) {
EMSG(_(e_prev_dir));
}
}
#endif
- homedir = vim_strsave(var);
+ homedir = vim_strsave((char_u *)var);
}
}
@@ -870,11 +869,12 @@ bool os_setenv_append_path(const char *fname)
return false;
}
const char *tail = (char *)path_tail_with_sep((char_u *)fname);
- const char *dir = (char *)vim_strnsave((char_u *)fname,
- (size_t)(tail - fname));
+ size_t dirlen = (size_t)(tail - fname);
+ assert(tail >= fname && dirlen + 1 < sizeof(os_buf));
+ xstrlcpy(os_buf, fname, dirlen + 1);
const char *path = os_getenv("PATH");
const size_t pathlen = path ? strlen(path) : 0;
- const size_t newlen = pathlen + strlen(dir) + 2;
+ const size_t newlen = pathlen + dirlen + 2;
if (newlen < MAX_ENVPATHLEN) {
char *temp = xmalloc(newlen);
if (pathlen == 0) {
@@ -883,7 +883,7 @@ bool os_setenv_append_path(const char *fname)
xstrlcpy(temp, path, newlen);
xstrlcat(temp, ENV_SEPSTR, newlen);
}
- xstrlcat(temp, dir, newlen);
+ xstrlcat(temp, os_buf, newlen);
os_setenv("PATH", temp, 1);
xfree(temp);
return true;
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 097c672887..e930561234 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -275,8 +275,8 @@ static bool is_executable(const char *name)
static bool is_executable_ext(char *name, const char *pathext)
FUNC_ATTR_NONNULL_ALL
{
- xstrlcpy((char *)NameBuff, name, sizeof(NameBuff));
- char *buf_end = xstrchrnul((char *)NameBuff, '\0');
+ xstrlcpy(os_buf, name, sizeof(os_buf));
+ char *buf_end = xstrchrnul(os_buf, '\0');
for (const char *ext = pathext; *ext; ext++) {
// Skip the extension if there is no suffix after a '.'.
if (ext[0] == '.' && (ext[1] == '\0' || ext[1] == ENV_SEPCHAR)) {
@@ -287,7 +287,7 @@ static bool is_executable_ext(char *name, const char *pathext)
const char *ext_end = xstrchrnul(ext, ENV_SEPCHAR);
STRLCPY(buf_end, ext, ext_end - ext + 1);
- if (is_executable((char *)NameBuff)) {
+ if (is_executable(os_buf)) {
return true;
}
diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c
index b57a69b82b..f5ba0c2612 100644
--- a/src/nvim/os/pty_process_unix.c
+++ b/src/nvim/os/pty_process_unix.c
@@ -33,7 +33,8 @@
# include "os/pty_process_unix.c.generated.h"
#endif
-bool pty_process_spawn(PtyProcess *ptyproc)
+/// @returns zero on success, or negative error code
+int pty_process_spawn(PtyProcess *ptyproc)
FUNC_ATTR_NONNULL_ALL
{
static struct termios termios;
@@ -41,6 +42,7 @@ bool pty_process_spawn(PtyProcess *ptyproc)
init_termios(&termios);
}
+ int status = 0; // zero or negative error code (libuv convention)
Process *proc = (Process *)ptyproc;
assert(!proc->err);
uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD);
@@ -50,8 +52,9 @@ bool pty_process_spawn(PtyProcess *ptyproc)
int pid = forkpty(&master, NULL, &termios, &ptyproc->winsize);
if (pid < 0) {
+ status = -errno;
ELOG("forkpty failed: %s", strerror(errno));
- return false;
+ return status;
} else if (pid == 0) {
init_child(ptyproc);
abort();
@@ -60,30 +63,34 @@ bool pty_process_spawn(PtyProcess *ptyproc)
// make sure the master file descriptor is non blocking
int master_status_flags = fcntl(master, F_GETFL);
if (master_status_flags == -1) {
+ status = -errno;
ELOG("Failed to get master descriptor status flags: %s", strerror(errno));
goto error;
}
if (fcntl(master, F_SETFL, master_status_flags | O_NONBLOCK) == -1) {
+ status = -errno;
ELOG("Failed to make master descriptor non-blocking: %s", strerror(errno));
goto error;
}
- if (proc->in && !set_duplicating_descriptor(master, &proc->in->uv.pipe)) {
+ if (proc->in
+ && (status = set_duplicating_descriptor(master, &proc->in->uv.pipe))) {
goto error;
}
- if (proc->out && !set_duplicating_descriptor(master, &proc->out->uv.pipe)) {
+ if (proc->out
+ && (status = set_duplicating_descriptor(master, &proc->out->uv.pipe))) {
goto error;
}
ptyproc->tty_fd = master;
proc->pid = pid;
- return true;
+ return 0;
error:
close(master);
kill(pid, SIGKILL);
waitpid(pid, NULL, 0);
- return false;
+ return status;
}
void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height)
@@ -137,9 +144,10 @@ static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL
return;
}
+ char *prog = ptyproc->process.argv[0];
setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1);
- execvp(ptyproc->process.argv[0], ptyproc->process.argv);
- fprintf(stderr, "execvp failed: %s\n", strerror(errno));
+ execvp(prog, ptyproc->process.argv);
+ fprintf(stderr, "execvp failed: %s: %s\n", strerror(errno), prog);
}
static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL
@@ -197,22 +205,24 @@ static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL
termios->c_cc[VTIME] = 0;
}
-static bool set_duplicating_descriptor(int fd, uv_pipe_t *pipe)
+static int set_duplicating_descriptor(int fd, uv_pipe_t *pipe)
FUNC_ATTR_NONNULL_ALL
{
+ int status = 0; // zero or negative error code (libuv convention)
int fd_dup = dup(fd);
if (fd_dup < 0) {
+ status = -errno;
ELOG("Failed to dup descriptor %d: %s", fd, strerror(errno));
- return false;
+ return status;
}
- int uv_result = uv_pipe_open(pipe, fd_dup);
- if (uv_result) {
+ status = uv_pipe_open(pipe, fd_dup);
+ if (status) {
ELOG("Failed to set pipe to descriptor %d: %s",
- fd_dup, uv_strerror(uv_result));
+ fd_dup, uv_strerror(status));
close(fd_dup);
- return false;
+ return status;
}
- return true;
+ return status;
}
static void chld_handler(uv_signal_t *handle, int signum)
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 9514936ad0..a5fd37ed5c 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -124,13 +124,9 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
size_t nread;
- int status = do_os_system(shell_build_argv((char *)cmd, (char *)extra_args),
- input.data,
- input.len,
- output_ptr,
- &nread,
- emsg_silent,
- forward_output);
+ int exitcode = do_os_system(shell_build_argv((char *)cmd, (char *)extra_args),
+ input.data, input.len, output_ptr, &nread,
+ emsg_silent, forward_output);
xfree(input.data);
@@ -139,16 +135,16 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
xfree(output);
}
- if (!emsg_silent && status != 0 && !(opts & kShellOptSilent)) {
+ if (!emsg_silent && exitcode != 0 && !(opts & kShellOptSilent)) {
MSG_PUTS(_("\nshell returned "));
- msg_outnum(status);
+ msg_outnum(exitcode);
msg_putchar('\n');
}
State = current_state;
signal_accept_deadly();
- return status;
+ return exitcode;
}
/// os_system - synchronously execute a command in the shell
@@ -157,7 +153,7 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
/// char *output = NULL;
/// size_t nread = 0;
/// char *argv[] = {"ls", "-la", NULL};
-/// int status = os_sytem(argv, NULL, 0, &output, &nread);
+/// int exitcode = os_sytem(argv, NULL, 0, &output, &nread);
///
/// @param argv The commandline arguments to be passed to the shell. `argv`
/// will be consumed.
@@ -218,11 +214,14 @@ static int do_os_system(char **argv,
proc->in = input != NULL ? &in : NULL;
proc->out = &out;
proc->err = &err;
- if (!process_spawn(proc)) {
+ int status = process_spawn(proc);
+ if (status) {
loop_poll_events(&main_loop, 0);
- // Failed, probably due to 'sh' not being executable
+ // Failed, probably 'shell' is not executable.
if (!silent) {
- MSG_PUTS(_("\nCannot execute "));
+ MSG_PUTS(_("\nshell failed to start: "));
+ msg_outtrans((char_u *)os_strerror(status));
+ MSG_PUTS(": ");
msg_outtrans((char_u *)prog);
msg_putchar('\n');
}
@@ -262,7 +261,7 @@ static int do_os_system(char **argv,
// busy state.
ui_busy_start();
ui_flush();
- int status = process_wait(proc, -1, NULL);
+ int exitcode = process_wait(proc, -1, NULL);
if (!got_int && out_data_decide_throttle(0)) {
// Last chunk of output was skipped; display it now.
out_data_ring(NULL, SIZE_MAX);
@@ -289,7 +288,7 @@ static int do_os_system(char **argv,
assert(multiqueue_empty(events));
multiqueue_free(events);
- return status;
+ return exitcode;
}
/// - ensures at least `desired` bytes in buffer
@@ -321,7 +320,7 @@ static void system_data_cb(Stream *stream, RBuffer *buf, size_t count,
/// Tracks output received for the current executing shell command, and displays
/// a pulsing "..." when output should be skipped. Tracking depends on the
/// synchronous/blocking nature of ":!".
-//
+///
/// Purpose:
/// 1. CTRL-C is more responsive. #1234 #5396
/// 2. Improves performance of :! (UI, esp. TUI, is the bottleneck).
diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po
index 61920697d0..2d99d67099 100644
--- a/src/nvim/po/fr.po
+++ b/src/nvim/po/fr.po
@@ -5264,12 +5264,21 @@ msgstr "Vim est un logiciel libre"
msgid "Help poor children in Uganda!"
msgstr "Aidez les enfants pauvres d'Ouganda !"
-msgid "type :help iccf<Enter> for information "
-msgstr "tapez :help iccf<Entre> pour plus d'informations "
+msgid "type :help nvim<Enter> if you are new! "
+msgstr "tapez :help nvim<Entre> si vous tes nouveau! "
+
+msgid "type :CheckHealth<Enter> to optimize Nvim"
+msgstr "tapez :CheckHealth<Entre> pour optimiser Nvim "
msgid "type :q<Enter> to exit "
msgstr "tapez :q<Entre> pour sortir du programme "
+msgid "type :help<Enter> for help "
+msgstr "tapez :help<Entre> pour obtenir de l'aide "
+
+msgid "type :help iccf<Enter> for information "
+msgstr "tapez :help iccf<Entre> pour plus d'informations "
+
msgid "type :help<Enter> or <F1> for on-line help"
msgstr "tapez :help<Entre> ou <F1> pour accder l'aide en ligne "
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 7bcc78e58a..721300c334 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -2,12 +2,13 @@
# Makefile to run all tests for Vim
#
-export SHELL := sh
-
-VIMPROG := ../../../build/bin/nvim
+NVIM_PRG ?= ../../../build/bin/nvim
SCRIPTSOURCE := ../../../runtime
-SCRIPTS := \
+export SHELL := sh
+export NVIM_PRG := $(NVIM_PRG)
+
+SCRIPTS ?= \
test13.out \
test14.out \
test17.out \
@@ -25,17 +26,20 @@ SCRIPTS := \
test79.out \
test_marks.out \
-# Tests using runtest.vim.vim.
+# Tests using runtest.vim.
# Keep test_alot*.res as the last one, sort the others.
-NEW_TESTS = \
+NEW_TESTS ?= \
test_bufwintabinfo.res \
test_cmdline.res \
test_cscope.res \
+ test_digraph.res \
test_diffmode.res \
test_gn.res \
test_hardcopy.res \
test_help_tagjump.res \
test_history.res \
+ test_increment.res \
+ test_increment_dbcs.res \
test_langmap.res \
test_match.res \
test_matchadd_conceal.res \
@@ -98,13 +102,13 @@ report:
echo ALL DONE; \
fi"
-test1.out: $(VIMPROG)
+test1.out: $(NVIM_PRG)
-$(SCRIPTS) $(SCRIPTS_GUI): $(VIMPROG) test1.out
+$(SCRIPTS) $(SCRIPTS_GUI): $(NVIM_PRG) test1.out
RM_ON_RUN := test.out X* viminfo
RM_ON_START := test.ok
-RUN_VIM := VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(TOOL) $(VIMPROG) -u unix.vim -U NONE -i viminfo --noplugin -s dotest.in
+RUN_VIM := VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE -i viminfo --noplugin -s dotest.in
clean:
-rm -rf *.out \
@@ -173,7 +177,7 @@ nolog:
# New style of tests uses Vim script with assert calls. These are easier
# to write and a lot easier to read and debug.
# Limitation: Only works with the +eval feature.
-RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(VIMPROG) -u unix.vim -U NONE --noplugin
+RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(NVIM_PRG) -u unix.vim -U NONE --noplugin
newtests: newtestssilent
@/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then \
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index eb5912086b..316aba968d 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -65,6 +65,7 @@ set shellslash
" Align with vim defaults.
set directory^=.
set nohidden
+set backspace=
function RunTheTest(test)
echo 'Executing ' . a:test
diff --git a/src/nvim/testdir/samples/memfile_test.c b/src/nvim/testdir/samples/memfile_test.c
new file mode 100644
index 0000000000..0fa1e14c40
--- /dev/null
+++ b/src/nvim/testdir/samples/memfile_test.c
@@ -0,0 +1,143 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * memfile_test.c: Unittests for memfile.c
+ * Mostly by Ivan Krasilnikov.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+
+/* Must include main.c because it contains much more than just main() */
+#define NO_VIM_MAIN
+#include "main.c"
+
+/* This file has to be included because the tested functions are static */
+#include "memfile.c"
+
+#define index_to_key(i) ((i) ^ 15167)
+#define TEST_COUNT 50000
+
+/*
+ * Test mf_hash_*() functions.
+ */
+ static void
+test_mf_hash(void)
+{
+ mf_hashtab_T ht;
+ mf_hashitem_T *item;
+ blocknr_T key;
+ long_u i;
+ long_u num_buckets;
+
+ mf_hash_init(&ht);
+
+ /* insert some items and check invariants */
+ for (i = 0; i < TEST_COUNT; i++)
+ {
+ assert(ht.mht_count == i);
+
+ /* check that number of buckets is a power of 2 */
+ num_buckets = ht.mht_mask + 1;
+ assert(num_buckets > 0 && (num_buckets & (num_buckets - 1)) == 0);
+
+ /* check load factor */
+ assert(ht.mht_count <= (num_buckets << MHT_LOG_LOAD_FACTOR));
+
+ if (i < (MHT_INIT_SIZE << MHT_LOG_LOAD_FACTOR))
+ {
+ /* first expansion shouldn't have occurred yet */
+ assert(num_buckets == MHT_INIT_SIZE);
+ assert(ht.mht_buckets == ht.mht_small_buckets);
+ }
+ else
+ {
+ assert(num_buckets > MHT_INIT_SIZE);
+ assert(ht.mht_buckets != ht.mht_small_buckets);
+ }
+
+ key = index_to_key(i);
+ assert(mf_hash_find(&ht, key) == NULL);
+
+ /* allocate and add new item */
+ item = (mf_hashitem_T *)lalloc_clear(sizeof(mf_hashtab_T), FALSE);
+ assert(item != NULL);
+ item->mhi_key = key;
+ mf_hash_add_item(&ht, item);
+
+ assert(mf_hash_find(&ht, key) == item);
+
+ if (ht.mht_mask + 1 != num_buckets)
+ {
+ /* hash table was expanded */
+ assert(ht.mht_mask + 1 == num_buckets * MHT_GROWTH_FACTOR);
+ assert(i + 1 == (num_buckets << MHT_LOG_LOAD_FACTOR));
+ }
+ }
+
+ /* check presence of inserted items */
+ for (i = 0; i < TEST_COUNT; i++)
+ {
+ key = index_to_key(i);
+ item = mf_hash_find(&ht, key);
+ assert(item != NULL);
+ assert(item->mhi_key == key);
+ }
+
+ /* delete some items */
+ for (i = 0; i < TEST_COUNT; i++)
+ {
+ if (i % 100 < 70)
+ {
+ key = index_to_key(i);
+ item = mf_hash_find(&ht, key);
+ assert(item != NULL);
+ assert(item->mhi_key == key);
+
+ mf_hash_rem_item(&ht, item);
+ assert(mf_hash_find(&ht, key) == NULL);
+
+ mf_hash_add_item(&ht, item);
+ assert(mf_hash_find(&ht, key) == item);
+
+ mf_hash_rem_item(&ht, item);
+ assert(mf_hash_find(&ht, key) == NULL);
+
+ vim_free(item);
+ }
+ }
+
+ /* check again */
+ for (i = 0; i < TEST_COUNT; i++)
+ {
+ key = index_to_key(i);
+ item = mf_hash_find(&ht, key);
+
+ if (i % 100 < 70)
+ {
+ assert(item == NULL);
+ }
+ else
+ {
+ assert(item != NULL);
+ assert(item->mhi_key == key);
+ }
+ }
+
+ /* free hash table and all remaining items */
+ mf_hash_free_all(&ht);
+}
+
+ int
+main(void)
+{
+ test_mf_hash();
+ return 0;
+}
diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim
index edd49a2b63..adbabd61b9 100644
--- a/src/nvim/testdir/test49.vim
+++ b/src/nvim/testdir/test49.vim
@@ -456,7 +456,7 @@ function! ExtraVim(...)
" messing up the user's viminfo file.
let redirect = a:0 ?
\ " -c 'au VimLeave * redir END' -c 'redir\\! >" . a:1 . "'" : ""
- exec "!echo '" . debug_quits . "q' | ../../../build/bin/nvim -u NONE -N -es" . redirect .
+ exec "!echo '" . debug_quits . "q' | $NVIM_PRG -u NONE -N -es" . redirect .
\ " -c 'debuggreedy|set viminfo+=nviminfo'" .
\ " -c 'let ExtraVimBegin = " . extra_begin . "'" .
\ " -c 'let ExtraVimResult = \"" . resultfile . "\"'" . breakpoints .
diff --git a/src/nvim/testdir/test_cscope.vim b/src/nvim/testdir/test_cscope.vim
index b6d70f0765..c8d2ebd7da 100644
--- a/src/nvim/testdir/test_cscope.vim
+++ b/src/nvim/testdir/test_cscope.vim
@@ -1,9 +1,262 @@
" Test for cscope commands.
-if !has('cscope')
+if !has('cscope') || !executable('cscope') || !has('quickfix')
finish
endif
+func CscopeSetupOrClean(setup)
+ if a:setup
+ noa sp samples/memfile_test.c
+ saveas! Xmemfile_test.c
+ call system('cscope -bk -fXcscope.out Xmemfile_test.c')
+ call system('cscope -bk -fXcscope2.out Xmemfile_test.c')
+ cscope add Xcscope.out
+ set cscopequickfix=s-,g-,d-,c-,t-,e-,f-,i-,a-
+ else
+ cscope kill -1
+ for file in ['Xcscope.out', 'Xcscope2.out', 'Xmemfile_test.c']
+ call delete(file)
+ endfo
+ endif
+endfunc
+
+func Test_cscopeWithCscopeConnections()
+ call CscopeSetupOrClean(1)
+ " Test 0: E568: duplicate cscope database not added
+ try
+ set nocscopeverbose
+ cscope add Xcscope.out
+ set cscopeverbose
+ catch
+ call assert_true(0)
+ endtry
+ call assert_fails('cscope add', 'E560')
+ call assert_fails('cscope add Xcscope.out', 'E568')
+ call assert_fails('cscope add doesnotexist.out', 'E563')
+
+ " Test 1: Find this C-Symbol
+ for cmd in ['cs find s main', 'cs find 0 main']
+ let a=execute(cmd)
+ " Test 1.1 test where it moves the cursor
+ call assert_equal('main(void)', getline('.'))
+ " Test 1.2 test the output of the :cs command
+ call assert_match('\n(1 of 1): <<main>> main(void )', a)
+ endfor
+
+ " Test 2: Find this definition
+ for cmd in ['cs find g test_mf_hash', 'cs find 1 test_mf_hash']
+ exe cmd
+ call assert_equal(['', '/*', ' * Test mf_hash_*() functions.', ' */', ' static void', 'test_mf_hash(void)', '{'], getline(line('.')-5, line('.')+1))
+ endfor
+
+ " Test 3: Find functions called by this function
+ for cmd in ['cs find d test_mf_hash', 'cs find 2 test_mf_hash']
+ let a=execute(cmd)
+ call assert_match('\n(1 of 42): <<mf_hash_init>> mf_hash_init(&ht);', a)
+ call assert_equal(' mf_hash_init(&ht);', getline('.'))
+ endfor
+
+ " Test 4: Find functions calling this function
+ for cmd in ['cs find c test_mf_hash', 'cs find 3 test_mf_hash']
+ let a=execute(cmd)
+ call assert_match('\n(1 of 1): <<main>> test_mf_hash();', a)
+ call assert_equal(' test_mf_hash();', getline('.'))
+ endfor
+
+ " Test 5: Find this text string
+ for cmd in ['cs find t Bram', 'cs find 4 Bram']
+ let a=execute(cmd)
+ call assert_match('(1 of 1): <<<unknown>>> \* VIM - Vi IMproved^Iby Bram Moolenaar', a)
+ call assert_equal(' * VIM - Vi IMproved by Bram Moolenaar', getline('.'))
+ endfor
+
+ " Test 6: Find this egrep pattern
+ " test all matches returned by cscope
+ for cmd in ['cs find e ^\#includ.', 'cs find 6 ^\#includ.']
+ let a=execute(cmd)
+ call assert_match('\n(1 of 3): <<<unknown>>> #include <assert.h>', a)
+ call assert_equal('#include <assert.h>', getline('.'))
+ cnext
+ call assert_equal('#include "main.c"', getline('.'))
+ cnext
+ call assert_equal('#include "memfile.c"', getline('.'))
+ call assert_fails('cnext', 'E553:')
+ endfor
+
+ " Test 7: Find the same egrep pattern using lcscope this time.
+ let a=execute('lcs find e ^\#includ.')
+ call assert_match('\n(1 of 3): <<<unknown>>> #include <assert.h>', a)
+ call assert_equal('#include <assert.h>', getline('.'))
+ lnext
+ call assert_equal('#include "main.c"', getline('.'))
+ lnext
+ call assert_equal('#include "memfile.c"', getline('.'))
+ call assert_fails('lnext', 'E553:')
+
+ " Test 8: Find this file
+ for cmd in ['cs find f Xmemfile_test.c', 'cs find 7 Xmemfile_test.c']
+ enew
+ let a=execute(cmd)
+ call assert_true(a =~ '"Xmemfile_test.c" \d\+L, \d\+C')
+ call assert_equal('Xmemfile_test.c', @%)
+ endfor
+
+ " Test 9: Find files #including this file
+ for cmd in ['cs find i assert.h', 'cs find 8 assert.h']
+ enew
+ let a=execute(cmd)
+ let alines = split(a, '\n', 1)
+ call assert_equal('', alines[0])
+ call assert_true(alines[1] =~ '"Xmemfile_test.c" \d\+L, \d\+C')
+ call assert_equal('(1 of 1): <<global>> #include <assert.h>', alines[2])
+ call assert_equal('#include <assert.h>', getline('.'))
+ endfor
+
+ " Test 10: Invalid find command
+ call assert_fails('cs find x', 'E560:')
+
+ " Test 11: Find places where this symbol is assigned a value
+ " this needs a cscope >= 15.8
+ " unfortunately, Travis has cscope version 15.7
+ let cscope_version=systemlist('cscope --version')[0]
+ let cs_version=str2float(matchstr(cscope_version, '\d\+\(\.\d\+\)\?'))
+ if cs_version >= 15.8
+ for cmd in ['cs find a item', 'cs find 9 item']
+ let a=execute(cmd)
+ call assert_equal(['', '(1 of 4): <<test_mf_hash>> item = (mf_hashitem_T *)lalloc_clear(sizeof(mf_hashtab_T), FALSE);'], split(a, '\n', 1))
+ call assert_equal(' item = (mf_hashitem_T *)lalloc_clear(sizeof(mf_hashtab_T), FALSE);', getline('.'))
+ cnext
+ call assert_equal(' item = mf_hash_find(&ht, key);', getline('.'))
+ cnext
+ call assert_equal(' item = mf_hash_find(&ht, key);', getline('.'))
+ cnext
+ call assert_equal(' item = mf_hash_find(&ht, key);', getline('.'))
+ endfor
+ endif
+
+ " Test 12: leading whitespace is not removed for cscope find text
+ let a=execute('cscope find t test_mf_hash')
+ call assert_equal(['', '(1 of 1): <<<unknown>>> test_mf_hash();'], split(a, '\n', 1))
+ call assert_equal(' test_mf_hash();', getline('.'))
+
+ " Test 13: test with scscope
+ let a=execute('scs find t Bram')
+ call assert_match('(1 of 1): <<<unknown>>> \* VIM - Vi IMproved^Iby Bram Moolenaar', a)
+ call assert_equal(' * VIM - Vi IMproved by Bram Moolenaar', getline('.'))
+
+ " Test 14: cscope help
+ for cmd in ['cs', 'cs help', 'cs xxx']
+ let a=execute(cmd)
+ call assert_match('^cscope commands:\n', a)
+ call assert_match('\nadd :', a)
+ call assert_match('\nfind :', a)
+ call assert_match('\nhelp : Show this message', a)
+ call assert_match('\nkill : Kill a connection', a)
+ call assert_match('\nreset: Reinit all connections', a)
+ call assert_match('\nshow : Show connections', a)
+ endfor
+ let a=execute('scscope help')
+ call assert_match('This cscope command does not support splitting the window\.', a)
+
+ " Test 15: reset connections
+ let a=execute('cscope reset')
+ call assert_match('\nAdded cscope database.*Xcscope.out (#0)', a)
+ call assert_match('\nAll cscope databases reset', a)
+
+ " Test 16: cscope show
+ let a=execute('cscope show')
+ call assert_match('\n 0 \d\+.*Xcscope.out\s*<none>', a)
+
+ " Test 17: cstag and 'csto' option
+ set csto=0
+ let a=execute('cstag TEST_COUNT')
+ call assert_match('(1 of 1): <<TEST_COUNT>> #define TEST_COUNT 50000', a)
+ call assert_equal('#define TEST_COUNT 50000', getline('.'))
+ set csto=1
+ let a=execute('cstag index_to_key')
+ call assert_match('(1 of 1): <<index_to_key>> #define index_to_key(i) ((i) ^ 15167)', a)
+ call assert_equal('#define index_to_key(i) ((i) ^ 15167)', getline('.'))
+ call assert_fails('cstag xxx', 'E257:')
+ call assert_fails('cstag', 'E562:')
+
+ " Test 18: 'cst' option
+ set nocst
+ call assert_fails('tag TEST_COUNT', 'E426:')
+ set cst
+ let a=execute('tag TEST_COUNT')
+ call assert_match('(1 of 1): <<TEST_COUNT>> #define TEST_COUNT 50000', a)
+ call assert_equal('#define TEST_COUNT 50000', getline('.'))
+ let a=execute('tags')
+ call assert_match('1 1 TEST_COUNT\s\+\d\+\s\+#define index_to_key', a)
+
+ " Test 19: this should trigger call to cs_print_tags()
+ " Unclear how to check result though, we just exercise the code.
+ set cst cscopequickfix=s0
+ call feedkeys(":cs find s main\<CR>", 't')
+
+ " Test 20: cscope kill
+ call assert_fails('cscope kill 2', 'E261:')
+ call assert_fails('cscope kill xxx', 'E261:')
+
+ let a=execute('cscope kill 0')
+ call assert_match('cscope connection 0 closed', a)
+
+ cscope add Xcscope.out
+ let a=execute('cscope kill Xcscope.out')
+ call assert_match('cscope connection Xcscope.out closed', a)
+
+ cscope add Xcscope.out .
+ let a=execute('cscope kill -1')
+ call assert_match('cscope connection .*Xcscope.out closed', a)
+ let a=execute('cscope kill -1')
+ call assert_equal('', a)
+
+ " Test 21: 'csprg' option
+ call assert_equal('cscope', &csprg)
+ set csprg=doesnotexist
+ call assert_fails('cscope add Xcscope2.out', 'E609:')
+ set csprg=cscope
+
+ " Test 22: multiple cscope connections
+ cscope add Xcscope.out
+ cscope add Xcscope2.out . -C
+ let a=execute('cscope show')
+ call assert_match('\n 0 \d\+.*Xcscope.out\s*<none>', a)
+ call assert_match('\n 1 \d\+.*Xcscope2.out\s*\.', a)
+
+ " Test 23: test Ex command line completion
+ call feedkeys(":cs \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"cs add find help kill reset show', @:)
+
+ call feedkeys(":scs \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"scs find', @:)
+
+ call feedkeys(":cs find \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"cs find a c d e f g i s t', @:)
+
+ call feedkeys(":cs kill \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"cs kill -1 0 1', @:)
+
+ call feedkeys(":cs add Xcscope\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"cs add Xcscope.out Xcscope2.out', @:)
+
+ " Test 24: cscope_connection()
+ call assert_equal(cscope_connection(), 1)
+ call assert_equal(cscope_connection(0, 'out'), 1)
+ call assert_equal(cscope_connection(0, 'xxx'), 1)
+ call assert_equal(cscope_connection(1, 'out'), 1)
+ call assert_equal(cscope_connection(1, 'xxx'), 0)
+ call assert_equal(cscope_connection(2, 'out'), 0)
+ call assert_equal(cscope_connection(3, 'xxx', '..'), 0)
+ call assert_equal(cscope_connection(3, 'out', 'xxx'), 0)
+ call assert_equal(cscope_connection(3, 'out', '.'), 1)
+ call assert_equal(cscope_connection(4, 'out', '.'), 0)
+
+ " CleanUp
+ call CscopeSetupOrClean(0)
+
+endfunc
+
func Test_cscopequickfix()
set cscopequickfix=s-,g-,d+,c-,t+,e-,f0,i-,a-
call assert_equal('s-,g-,d+,c-,t+,e-,f0,i-,a-', &cscopequickfix)
@@ -13,3 +266,14 @@ func Test_cscopequickfix()
call assert_fails('set cscopequickfix=s7', 'E474:')
call assert_fails('set cscopequickfix=s-a', 'E474:')
endfunc
+
+func Test_withoutCscopeConnection()
+ call assert_equal(cscope_connection(), 0)
+
+ call assert_fails('cscope find s main', 'E567:')
+ let a=execute('cscope show')
+ call assert_match('no cscope connections', a)
+endfunc
+
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 7666594862..5de394de8e 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -202,3 +202,19 @@ func Test_diffget_diffput()
bwipe!
enew!
endfunc
+
+func Test_diffoff()
+ enew!
+ call setline(1, ['Two', 'Three'])
+ let normattr = screenattr(1, 1)
+ diffthis
+ botright vert new
+ call setline(1, ['One', '', 'Two', 'Three'])
+ diffthis
+ redraw
+ diffoff!
+ redraw
+ call assert_equal(normattr, screenattr(1, 1))
+ bwipe!
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim
new file mode 100644
index 0000000000..95500853f2
--- /dev/null
+++ b/src/nvim/testdir/test_digraph.vim
@@ -0,0 +1,461 @@
+" Tests for digraphs
+
+if !has("digraphs") || !has("multi_byte")
+ finish
+endif
+
+func! Put_Dig(chars)
+ exe "norm! o\<c-k>".a:chars
+endfu
+
+func! Put_Dig_BS(char1, char2)
+ exe "norm! o".a:char1."\<bs>".a:char2
+endfu
+
+func! Test_digraphs()
+ new
+ call Put_Dig("00")
+ call assert_equal("∞", getline('.'))
+ " not a digraph
+ call Put_Dig("el")
+ call assert_equal("l", getline('.'))
+ call Put_Dig("ht")
+ call assert_equal("þ", getline('.'))
+ " digraph "ab" is the same as "ba"
+ call Put_Dig("ab")
+ call Put_Dig("ba")
+ call assert_equal(["ば","ば"], getline(line('.')-1,line('.')))
+ " Euro sign
+ call Put_Dig("e=")
+ call Put_Dig("=e")
+ call Put_Dig("Eu")
+ call Put_Dig("uE")
+ call assert_equal(['е']+repeat(["€"],3), getline(line('.')-3,line('.')))
+ " Rouble sign
+ call Put_Dig("R=")
+ call Put_Dig("=R")
+ call Put_Dig("=P")
+ call Put_Dig("P=")
+ call assert_equal(['Р']+repeat(["₽"],2)+['П'], getline(line('.')-3,line('.')))
+ " Not a digraph
+ call Put_Dig("a\<bs>")
+ call Put_Dig("\<bs>a")
+ call assert_equal(["<BS>", "<BS>a"], getline(line('.')-1,line('.')))
+ " Grave
+ call Put_Dig("a!")
+ call Put_Dig("!e")
+ call Put_Dig("b!") " not defined
+ call assert_equal(["à", "è", "!"], getline(line('.')-2,line('.')))
+ " Acute accent
+ call Put_Dig("a'")
+ call Put_Dig("'e")
+ call Put_Dig("b'") " not defined
+ call assert_equal(["á", "é", "'"], getline(line('.')-2,line('.')))
+ " Cicumflex
+ call Put_Dig("a>")
+ call Put_Dig(">e")
+ call Put_Dig("b>") " not defined
+ call assert_equal(['â', 'ê', '>'], getline(line('.')-2,line('.')))
+ " Tilde
+ call Put_Dig("o~")
+ call Put_Dig("~u") " not defined
+ call Put_Dig("z~") " not defined
+ call assert_equal(['õ', 'u', '~'], getline(line('.')-2,line('.')))
+ " Tilde
+ call Put_Dig("o?")
+ call Put_Dig("?u")
+ call Put_Dig("z?") " not defined
+ call assert_equal(['õ', 'ũ', '?'], getline(line('.')-2,line('.')))
+ " Macron
+ call Put_Dig("o-")
+ call Put_Dig("-u")
+ call Put_Dig("z-") " not defined
+ call assert_equal(['ō', 'ū', '-'], getline(line('.')-2,line('.')))
+ " Breve
+ call Put_Dig("o(")
+ call Put_Dig("(u")
+ call Put_Dig("z(") " not defined
+ call assert_equal(['ŏ', 'ŭ', '('], getline(line('.')-2,line('.')))
+ " Dot above
+ call Put_Dig("b.")
+ call Put_Dig(".e")
+ call Put_Dig("a.") " not defined
+ call assert_equal(['ḃ', 'ė', '.'], getline(line('.')-2,line('.')))
+ " Diaresis
+ call Put_Dig("a:")
+ call Put_Dig(":u")
+ call Put_Dig("b:") " not defined
+ call assert_equal(['ä', 'ü', ':'], getline(line('.')-2,line('.')))
+ " Cedilla
+ call Put_Dig("',")
+ call Put_Dig(",C")
+ call Put_Dig("b,") " not defined
+ call assert_equal(['¸', 'Ç', ','], getline(line('.')-2,line('.')))
+ " Underline
+ call Put_Dig("B_")
+ call Put_Dig("_t")
+ call Put_Dig("a_") " not defined
+ call assert_equal(['Ḇ', 'ṯ', '_'], getline(line('.')-2,line('.')))
+ " Stroke
+ call Put_Dig("j/")
+ call Put_Dig("/l")
+ call Put_Dig("b/") " not defined
+ call assert_equal(['/', 'ł', '/'], getline(line('.')-2,line('.')))
+ " Double acute
+ call Put_Dig('O"')
+ call Put_Dig('"y')
+ call Put_Dig('b"') " not defined
+ call assert_equal(['Ő', 'ÿ', '"'], getline(line('.')-2,line('.')))
+ " Ogonek
+ call Put_Dig('u;')
+ call Put_Dig(';E')
+ call Put_Dig('b;') " not defined
+ call assert_equal(['ų', 'Ę', ';'], getline(line('.')-2,line('.')))
+ " Caron
+ call Put_Dig('u<')
+ call Put_Dig('<E')
+ call Put_Dig('b<') " not defined
+ call assert_equal(['ǔ', 'Ě', '<'], getline(line('.')-2,line('.')))
+ " Ring above
+ call Put_Dig('u0')
+ call Put_Dig('0E') " not defined
+ call Put_Dig('b0') " not defined
+ call assert_equal(['ů', 'E', '0'], getline(line('.')-2,line('.')))
+ " Hook
+ call Put_Dig('u2')
+ call Put_Dig('2E')
+ call Put_Dig('b2') " not defined
+ call assert_equal(['ủ', 'Ẻ', '2'], getline(line('.')-2,line('.')))
+ " Horn
+ call Put_Dig('u9')
+ call Put_Dig('9E') " not defined
+ call Put_Dig('b9') " not defined
+ call assert_equal(['ư', 'E', '9'], getline(line('.')-2,line('.')))
+ " Cyrillic
+ call Put_Dig('u=')
+ call Put_Dig('=b')
+ call Put_Dig('=_')
+ call assert_equal(['у', 'б', '〓'], getline(line('.')-2,line('.')))
+ " Greek
+ call Put_Dig('u*')
+ call Put_Dig('*b')
+ call Put_Dig('*_')
+ call assert_equal(['υ', 'β', '々'], getline(line('.')-2,line('.')))
+ " Greek/Cyrillic special
+ call Put_Dig('u%')
+ call Put_Dig('%b') " not defined
+ call Put_Dig('%_') " not defined
+ call assert_equal(['ύ', 'b', '_'], getline(line('.')-2,line('.')))
+ " Arabic
+ call Put_Dig('u+')
+ call Put_Dig('+b')
+ call Put_Dig('+_') " japanese industrial symbol
+ call assert_equal(['+', 'ب', '〄'], getline(line('.')-2,line('.')))
+ " Hebrew
+ call Put_Dig('Q+')
+ call Put_Dig('+B')
+ call Put_Dig('+X')
+ call assert_equal(['ק', 'ב', 'ח'], getline(line('.')-2,line('.')))
+ " Latin
+ call Put_Dig('a3')
+ call Put_Dig('A3')
+ call Put_Dig('3X')
+ call assert_equal(['ǣ', 'Ǣ', 'X'], getline(line('.')-2,line('.')))
+ " Bopomofo
+ call Put_Dig('a4')
+ call Put_Dig('A4')
+ call Put_Dig('4X')
+ call assert_equal(['ㄚ', '4', 'X'], getline(line('.')-2,line('.')))
+ " Hiragana
+ call Put_Dig('a5')
+ call Put_Dig('A5')
+ call Put_Dig('5X')
+ call assert_equal(['あ', 'ぁ', 'X'], getline(line('.')-2,line('.')))
+ " Katakana
+ call Put_Dig('a6')
+ call Put_Dig('A6')
+ call Put_Dig('6X')
+ call assert_equal(['ァ', 'ア', 'X'], getline(line('.')-2,line('.')))
+ " Superscripts
+ call Put_Dig('1S')
+ call Put_Dig('2S')
+ call Put_Dig('3S')
+ call assert_equal(['¹', '²', '³'], getline(line('.')-2,line('.')))
+ " Subscripts
+ call Put_Dig('1s')
+ call Put_Dig('2s')
+ call Put_Dig('3s')
+ call assert_equal(['₁', '₂', '₃'], getline(line('.')-2,line('.')))
+ " Eszet (only lowercase)
+ call Put_Dig("ss")
+ call Put_Dig("SS") " start of string
+ call assert_equal(["ß", "˜"], getline(line('.')-1,line('.')))
+ " High bit set
+ call Put_Dig("a ")
+ call Put_Dig(" A")
+ call assert_equal(['á', 'Á'], getline(line('.')-1,line('.')))
+ " Escape is not part of a digraph
+ call Put_Dig("a\<esc>")
+ call Put_Dig("\<esc>A")
+ call assert_equal(['', 'A'], getline(line('.')-1,line('.')))
+ " define some custom digraphs
+ " old: 00 ∞
+ " old: el l
+ digraph 00 9216
+ digraph el 0252
+ call Put_Dig("00")
+ call Put_Dig("el")
+ " Reset digraphs
+ digraph 00 8734
+ digraph el 108
+ call Put_Dig("00")
+ call Put_Dig("el")
+ call assert_equal(['␀', 'ü', '∞', 'l'], getline(line('.')-3,line('.')))
+ bw!
+endfunc
+
+func! Test_digraphs_option()
+ " reset whichwrap option, so that testing <esc><bs>A works,
+ " without moving up a line
+ set digraph ww=
+ new
+ call Put_Dig_BS("0","0")
+ call assert_equal("∞", getline('.'))
+ " not a digraph
+ call Put_Dig_BS("e","l")
+ call assert_equal("l", getline('.'))
+ call Put_Dig_BS("h","t")
+ call assert_equal("þ", getline('.'))
+ " digraph "ab" is the same as "ba"
+ call Put_Dig_BS("a","b")
+ call Put_Dig_BS("b","a")
+ call assert_equal(["ば","ば"], getline(line('.')-1,line('.')))
+ " Euro sign
+ call Put_Dig_BS("e","=")
+ call Put_Dig_BS("=","e")
+ call Put_Dig_BS("E","u")
+ call Put_Dig_BS("u","E")
+ call assert_equal(['е']+repeat(["€"],3), getline(line('.')-3,line('.')))
+ " Rouble sign
+ call Put_Dig_BS("R","=")
+ call Put_Dig_BS("=","R")
+ call Put_Dig_BS("=","P")
+ call Put_Dig_BS("P","=")
+ call assert_equal(['Р']+repeat(["₽"],2)+['П'], getline(line('.')-3,line('.')))
+ " Not a digraph: this is different from <c-k>!
+ call Put_Dig_BS("a","\<bs>")
+ call Put_Dig_BS("\<bs>","a")
+ call assert_equal(['','a'], getline(line('.')-1,line('.')))
+ " Grave
+ call Put_Dig_BS("a","!")
+ call Put_Dig_BS("!","e")
+ call Put_Dig_BS("b","!") " not defined
+ call assert_equal(["à", "è", "!"], getline(line('.')-2,line('.')))
+ " Acute accent
+ call Put_Dig_BS("a","'")
+ call Put_Dig_BS("'","e")
+ call Put_Dig_BS("b","'") " not defined
+ call assert_equal(["á", "é", "'"], getline(line('.')-2,line('.')))
+ " Cicumflex
+ call Put_Dig_BS("a",">")
+ call Put_Dig_BS(">","e")
+ call Put_Dig_BS("b",">") " not defined
+ call assert_equal(['â', 'ê', '>'], getline(line('.')-2,line('.')))
+ " Tilde
+ call Put_Dig_BS("o","~")
+ call Put_Dig_BS("~","u") " not defined
+ call Put_Dig_BS("z","~") " not defined
+ call assert_equal(['õ', 'u', '~'], getline(line('.')-2,line('.')))
+ " Tilde
+ call Put_Dig_BS("o","?")
+ call Put_Dig_BS("?","u")
+ call Put_Dig_BS("z","?") " not defined
+ call assert_equal(['õ', 'ũ', '?'], getline(line('.')-2,line('.')))
+ " Macron
+ call Put_Dig_BS("o","-")
+ call Put_Dig_BS("-","u")
+ call Put_Dig_BS("z","-") " not defined
+ call assert_equal(['ō', 'ū', '-'], getline(line('.')-2,line('.')))
+ " Breve
+ call Put_Dig_BS("o","(")
+ call Put_Dig_BS("(","u")
+ call Put_Dig_BS("z","(") " not defined
+ call assert_equal(['ŏ', 'ŭ', '('], getline(line('.')-2,line('.')))
+ " Dot above
+ call Put_Dig_BS("b",".")
+ call Put_Dig_BS(".","e")
+ call Put_Dig_BS("a",".") " not defined
+ call assert_equal(['ḃ', 'ė', '.'], getline(line('.')-2,line('.')))
+ " Diaresis
+ call Put_Dig_BS("a",":")
+ call Put_Dig_BS(":","u")
+ call Put_Dig_BS("b",":") " not defined
+ call assert_equal(['ä', 'ü', ':'], getline(line('.')-2,line('.')))
+ " Cedilla
+ call Put_Dig_BS("'",",")
+ call Put_Dig_BS(",","C")
+ call Put_Dig_BS("b",",") " not defined
+ call assert_equal(['¸', 'Ç', ','], getline(line('.')-2,line('.')))
+ " Underline
+ call Put_Dig_BS("B","_")
+ call Put_Dig_BS("_","t")
+ call Put_Dig_BS("a","_") " not defined
+ call assert_equal(['Ḇ', 'ṯ', '_'], getline(line('.')-2,line('.')))
+ " Stroke
+ call Put_Dig_BS("j","/")
+ call Put_Dig_BS("/","l")
+ call Put_Dig_BS("b","/") " not defined
+ call assert_equal(['/', 'ł', '/'], getline(line('.')-2,line('.')))
+ " Double acute
+ call Put_Dig_BS('O','"')
+ call Put_Dig_BS('"','y')
+ call Put_Dig_BS('b','"') " not defined
+ call assert_equal(['Ő', 'ÿ', '"'], getline(line('.')-2,line('.')))
+ " Ogonek
+ call Put_Dig_BS('u',';')
+ call Put_Dig_BS(';','E')
+ call Put_Dig_BS('b',';') " not defined
+ call assert_equal(['ų', 'Ę', ';'], getline(line('.')-2,line('.')))
+ " Caron
+ call Put_Dig_BS('u','<')
+ call Put_Dig_BS('<','E')
+ call Put_Dig_BS('b','<') " not defined
+ call assert_equal(['ǔ', 'Ě', '<'], getline(line('.')-2,line('.')))
+ " Ring above
+ call Put_Dig_BS('u','0')
+ call Put_Dig_BS('0','E') " not defined
+ call Put_Dig_BS('b','0') " not defined
+ call assert_equal(['ů', 'E', '0'], getline(line('.')-2,line('.')))
+ " Hook
+ call Put_Dig_BS('u','2')
+ call Put_Dig_BS('2','E')
+ call Put_Dig_BS('b','2') " not defined
+ call assert_equal(['ủ', 'Ẻ', '2'], getline(line('.')-2,line('.')))
+ " Horn
+ call Put_Dig_BS('u','9')
+ call Put_Dig_BS('9','E') " not defined
+ call Put_Dig_BS('b','9') " not defined
+ call assert_equal(['ư', 'E', '9'], getline(line('.')-2,line('.')))
+ " Cyrillic
+ call Put_Dig_BS('u','=')
+ call Put_Dig_BS('=','b')
+ call Put_Dig_BS('=','_')
+ call assert_equal(['у', 'б', '〓'], getline(line('.')-2,line('.')))
+ " Greek
+ call Put_Dig_BS('u','*')
+ call Put_Dig_BS('*','b')
+ call Put_Dig_BS('*','_')
+ call assert_equal(['υ', 'β', '々'], getline(line('.')-2,line('.')))
+ " Greek/Cyrillic special
+ call Put_Dig_BS('u','%')
+ call Put_Dig_BS('%','b') " not defined
+ call Put_Dig_BS('%','_') " not defined
+ call assert_equal(['ύ', 'b', '_'], getline(line('.')-2,line('.')))
+ " Arabic
+ call Put_Dig_BS('u','+')
+ call Put_Dig_BS('+','b')
+ call Put_Dig_BS('+','_') " japanese industrial symbol
+ call assert_equal(['+', 'ب', '〄'], getline(line('.')-2,line('.')))
+ " Hebrew
+ call Put_Dig_BS('Q','+')
+ call Put_Dig_BS('+','B')
+ call Put_Dig_BS('+','X')
+ call assert_equal(['ק', 'ב', 'ח'], getline(line('.')-2,line('.')))
+ " Latin
+ call Put_Dig_BS('a','3')
+ call Put_Dig_BS('A','3')
+ call Put_Dig_BS('3','X')
+ call assert_equal(['ǣ', 'Ǣ', 'X'], getline(line('.')-2,line('.')))
+ " Bopomofo
+ call Put_Dig_BS('a','4')
+ call Put_Dig_BS('A','4')
+ call Put_Dig_BS('4','X')
+ call assert_equal(['ㄚ', '4', 'X'], getline(line('.')-2,line('.')))
+ " Hiragana
+ call Put_Dig_BS('a','5')
+ call Put_Dig_BS('A','5')
+ call Put_Dig_BS('5','X')
+ call assert_equal(['あ', 'ぁ', 'X'], getline(line('.')-2,line('.')))
+ " Katakana
+ call Put_Dig_BS('a','6')
+ call Put_Dig_BS('A','6')
+ call Put_Dig_BS('6','X')
+ call assert_equal(['ァ', 'ア', 'X'], getline(line('.')-2,line('.')))
+ " Superscripts
+ call Put_Dig_BS('1','S')
+ call Put_Dig_BS('2','S')
+ call Put_Dig_BS('3','S')
+ call assert_equal(['¹', '²', '³'], getline(line('.')-2,line('.')))
+ " Subscripts
+ call Put_Dig_BS('1','s')
+ call Put_Dig_BS('2','s')
+ call Put_Dig_BS('3','s')
+ call assert_equal(['₁', '₂', '₃'], getline(line('.')-2,line('.')))
+ " Eszet (only lowercase)
+ call Put_Dig_BS("s","s")
+ call Put_Dig_BS("S","S") " start of string
+ call assert_equal(["ß", "˜"], getline(line('.')-1,line('.')))
+ " High bit set (different from <c-k>)
+ call Put_Dig_BS("a"," ")
+ call Put_Dig_BS(" ","A")
+ call assert_equal([' ', 'A'], getline(line('.')-1,line('.')))
+ " Escape is not part of a digraph (different from <c-k>)
+ call Put_Dig_BS("a","\<esc>")
+ call Put_Dig_BS("\<esc>","A")
+ call assert_equal(['', ''], getline(line('.')-1,line('.')))
+ " define some custom digraphs
+ " old: 00 ∞
+ " old: el l
+ digraph 00 9216
+ digraph el 0252
+ call Put_Dig_BS("0","0")
+ call Put_Dig_BS("e","l")
+ " Reset digraphs
+ digraph 00 8734
+ digraph el 108
+ call Put_Dig_BS("0","0")
+ call Put_Dig_BS("e","l")
+ call assert_equal(['␀', 'ü', '∞', 'l'], getline(line('.')-3,line('.')))
+ set nodigraph ww&vim
+ bw!
+endfunc
+
+func! Test_digraphs_output()
+ new
+ let out = execute(':digraph')
+ call assert_equal('Eu € 8364', matchstr(out, '\C\<Eu\D*8364\>'))
+ call assert_equal('=e € 8364', matchstr(out, '\C=e\D*8364\>'))
+ call assert_equal('=R ₽ 8381', matchstr(out, '\C=R\D*8381\>'))
+ call assert_equal('=P ₽ 8381', matchstr(out, '\C=P\D*8381\>'))
+ call assert_equal('o: ö 246', matchstr(out, '\C\<o:\D*246\>'))
+ call assert_equal('v4 ㄪ 12586', matchstr(out, '\C\<v4\D*12586\>'))
+ call assert_equal("'0 ˚ 730", matchstr(out, '\C''0\D*730\>'))
+ call assert_equal('Z% Ж 1046', matchstr(out, '\C\<Z%\D*1046\>'))
+ call assert_equal('u- ū 363', matchstr(out, '\C\<u-\D*363\>'))
+ call assert_equal('SH ^A 1', matchstr(out, '\C\<SH\D*1\>'))
+ bw!
+endfunc
+
+func! Test_loadkeymap()
+ if !has('keymap')
+ return
+ endif
+ new
+ set keymap=czech
+ set iminsert=0
+ call feedkeys("o|\<c-^>|01234567890|\<esc>", 'tx')
+ call assert_equal("|'é+ěščřžýáíé'", getline('.'))
+ " reset keymap and encoding option
+ set keymap=
+ bw!
+endfunc
+
+func! Test_digraph_cmndline()
+ " Create digraph on commandline
+ " This is a hack, to let Vim create the digraph in commandline mode
+ let s = ''
+ exe "sil! norm! :let s.='\<c-k>Eu'\<cr>"
+ call assert_equal("€", s)
+endfunc
+
+" vim: tabstop=2 shiftwidth=0 sts=-1 expandtab
diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim
new file mode 100644
index 0000000000..e53b569716
--- /dev/null
+++ b/src/nvim/testdir/test_increment.vim
@@ -0,0 +1,760 @@
+" Tests for using Ctrl-A/Ctrl-X on visual selections
+
+func SetUp()
+ new dummy
+ set nrformats&vim
+endfunc
+
+func TearDown()
+ bwipe!
+endfunc
+
+" 1) Ctrl-A on visually selected number
+" Text:
+" foobar-10
+" Expected:
+" 1) Ctrl-A on start of line:
+" foobar-9
+" 2) Ctrl-A on visually selected "-10":
+" foobar-9
+" 3) Ctrl-A on visually selected "10":
+" foobar-11
+" 4) Ctrl-X on visually selected "-10"
+" foobar-11
+" 5) Ctrl-X on visually selected "10"
+" foobar-9
+func Test_visual_increment_01()
+ call setline(1, repeat(["foobaar-10"], 5))
+
+ call cursor(1, 1)
+ exec "norm! \<C-A>"
+ call assert_equal("foobaar-9", getline('.'))
+ call assert_equal([0, 1, 9, 0], getpos('.'))
+
+ call cursor(2, 1)
+ exec "norm! f-v$\<C-A>"
+ call assert_equal("foobaar-9", getline('.'))
+ call assert_equal([0, 2, 8, 0], getpos('.'))
+
+ call cursor(3, 1)
+ exec "norm! f1v$\<C-A>"
+ call assert_equal("foobaar-11", getline('.'))
+ call assert_equal([0, 3, 9, 0], getpos('.'))
+
+ call cursor(4, 1)
+ exec "norm! f-v$\<C-X>"
+ call assert_equal("foobaar-11", getline('.'))
+ call assert_equal([0, 4, 8, 0], getpos('.'))
+
+ call cursor(5, 1)
+ exec "norm! f1v$\<C-X>"
+ call assert_equal("foobaar-9", getline('.'))
+ call assert_equal([0, 5, 9, 0], getpos('.'))
+endfunc
+
+" 2) Ctrl-A on visually selected lines
+" Text:
+" 10
+" 20
+" 30
+" 40
+"
+" Expected:
+" 1) Ctrl-A on visually selected lines:
+" 11
+" 21
+" 31
+" 41
+"
+" 2) Ctrl-X on visually selected lines:
+" 9
+" 19
+" 29
+" 39
+func Test_visual_increment_02()
+ call setline(1, ["10", "20", "30", "40"])
+ exec "norm! GV3k$\<C-A>"
+ call assert_equal(["11", "21", "31", "41"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+
+ call setline(1, ["10", "20", "30", "40"])
+ exec "norm! GV3k$\<C-X>"
+ call assert_equal(["9", "19", "29", "39"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 3) g Ctrl-A on visually selected lines, with non-numbers in between
+" Text:
+" 10
+"
+" 20
+"
+" 30
+"
+" 40
+"
+" Expected:
+" 1) 2 g Ctrl-A on visually selected lines:
+" 12
+"
+" 24
+"
+" 36
+"
+" 48
+" 2) 2 g Ctrl-X on visually selected lines
+" 8
+"
+" 16
+"
+" 24
+"
+" 32
+func Test_visual_increment_03()
+ call setline(1, ["10", "", "20", "", "30", "", "40"])
+ exec "norm! GV6k2g\<C-A>"
+ call assert_equal(["12", "", "24", "", "36", "", "48"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+
+ call setline(1, ["10", "", "20", "", "30", "", "40"])
+ exec "norm! GV6k2g\<C-X>"
+ call assert_equal(["8", "", "16", "", "24", "", "32"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 4) Ctrl-A on non-number
+" Text:
+" foobar-10
+" Expected:
+" 1) visually select foobar:
+" foobar-10
+func Test_visual_increment_04()
+ call setline(1, ["foobar-10"])
+ exec "norm! vf-\<C-A>"
+ call assert_equal(["foobar-10"], getline(1, '$'))
+ " NOTE: I think this is correct behavior...
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 5) g<Ctrl-A> on letter
+" Test:
+" a
+" a
+" a
+" a
+" Expected:
+" 1) g Ctrl-A on visually selected lines
+" b
+" c
+" d
+" e
+func Test_visual_increment_05()
+ set nrformats+=alpha
+ call setline(1, repeat(["a"], 4))
+ exec "norm! GV3kg\<C-A>"
+ call assert_equal(["b", "c", "d", "e"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 6) g<Ctrl-A> on letter
+" Test:
+" z
+" z
+" z
+" z
+" Expected:
+" 1) g Ctrl-X on visually selected lines
+" y
+" x
+" w
+" v
+func Test_visual_increment_06()
+ set nrformats+=alpha
+ call setline(1, repeat(["z"], 4))
+ exec "norm! GV3kg\<C-X>"
+ call assert_equal(["y", "x", "w", "v"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 7) <Ctrl-A> on letter
+" Test:
+" 2
+" 1
+" 0
+" -1
+" -2
+"
+" Expected:
+" 1) Ctrl-A on visually selected lines
+" 3
+" 2
+" 1
+" 0
+" -1
+"
+" 2) Ctrl-X on visually selected lines
+" 1
+" 0
+" -1
+" -2
+" -3
+func Test_visual_increment_07()
+ call setline(1, ["2", "1", "0", "-1", "-2"])
+ exec "norm! GV4k\<C-A>"
+ call assert_equal(["3", "2", "1", "0", "-1"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+
+ call setline(1, ["2", "1", "0", "-1", "-2"])
+ exec "norm! GV4k\<C-X>"
+ call assert_equal(["1", "0", "-1", "-2", "-3"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 8) Block increment on 0x9
+" Text:
+" 0x9
+" 0x9
+" Expected:
+" 1) Ctrl-A on visually block selected region (cursor at beginning):
+" 0xa
+" 0xa
+" 2) Ctrl-A on visually block selected region (cursor at end)
+" 0xa
+" 0xa
+func Test_visual_increment_08()
+ call setline(1, repeat(["0x9"], 2))
+ exec "norm! \<C-V>j$\<C-A>"
+ call assert_equal(["0xa", "0xa"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+
+ call setline(1, repeat(["0x9"], 2))
+ exec "norm! gg$\<C-V>+\<C-A>"
+ call assert_equal(["0xa", "0xa"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 9) Increment and redo
+" Text:
+" 2
+" 2
+"
+" 3
+" 3
+"
+" Expected:
+" 1) 2 Ctrl-A on first 2 visually selected lines
+" 4
+" 4
+" 2) redo (.) on 3
+" 5
+" 5
+func Test_visual_increment_09()
+ call setline(1, ["2", "2", "", "3", "3", ""])
+ exec "norm! ggVj2\<C-A>"
+ call assert_equal(["4", "4", "", "3", "3", ""], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+
+ exec "norm! 3j."
+ call assert_equal(["4", "4", "", "5", "5", ""], getline(1, '$'))
+ call assert_equal([0, 4, 1, 0], getpos('.'))
+endfunc
+
+" 10) sequentially decrement 1
+" Text:
+" 1
+" 1
+" 1
+" 1
+" Expected:
+" 1) g Ctrl-X on visually selected lines
+" 0
+" -1
+" -2
+" -3
+func Test_visual_increment_10()
+ call setline(1, repeat(["1"], 4))
+ exec "norm! GV3kg\<C-X>"
+ call assert_equal(["0", "-1", "-2", "-3"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 11) visually block selected indented lines
+" Text:
+" 1
+" 1
+" 1
+" 1
+" Expexted:
+" 1) g Ctrl-A on block selected indented lines
+" 2
+" 1
+" 3
+" 4
+func Test_visual_increment_11()
+ call setline(1, [" 1", "1", " 1", " 1"])
+ exec "norm! f1\<C-V>3jg\<C-A>"
+ call assert_equal([" 2", "1", " 3", " 4"], getline(1, '$'))
+ call assert_equal([0, 1, 5, 0], getpos('.'))
+endfunc
+
+" 12) visually selected several columns
+" Text:
+" 0 0
+" 0 0
+" 0 0
+" Expected:
+" 1) 'v' select last zero and first zeroes
+" 0 1
+" 1 0
+" 1 0
+func Test_visual_increment_12()
+ call setline(1, repeat(["0 0"], 3))
+ exec "norm! $v++\<C-A>"
+ call assert_equal(["0 1", "1 0", "1 0"], getline(1, '$'))
+ call assert_equal([0, 1, 3, 0], getpos('.'))
+endfunc
+
+" 13) visually selected part of columns
+" Text:
+" max: 100px
+" max: 200px
+" max: 300px
+" max: 400px
+" Expected:
+" 1) 'v' on first two numbers Ctrl-A
+" max: 110px
+" max: 220px
+" max: 330px
+" max: 400px
+" 2) 'v' on first two numbers Ctrl-X
+" max: 90px
+" max: 190px
+" max: 290px
+" max: 400px
+func Test_visual_increment_13()
+ call setline(1, ["max: 100px", "max: 200px", "max: 300px", "max: 400px"])
+ exec "norm! f1\<C-V>l2j\<C-A>"
+ call assert_equal(["max: 110px", "max: 210px", "max: 310px", "max: 400px"], getline(1, '$'))
+ call assert_equal([0, 1, 6, 0], getpos('.'))
+
+ call setline(1, ["max: 100px", "max: 200px", "max: 300px", "max: 400px"])
+ exec "norm! ggf1\<C-V>l2j\<C-X>"
+ call assert_equal(["max: 90px", "max: 190px", "max: 290px", "max: 400px"], getline(1, '$'))
+ call assert_equal([0, 1, 6, 0], getpos('.'))
+endfunc
+
+" 14) redo in block mode
+" Text:
+" 1 1
+" 1 1
+" Expected:
+" 1) Ctrl-a on first column, redo on second column
+" 2 2
+" 2 2
+func Test_visual_increment_14()
+ call setline(1, repeat(["1 1"], 2))
+ exec "norm! G\<C-V>k\<C-A>w."
+ call assert_equal(["2 2", "2 2"], getline(1, '$'))
+ call assert_equal([0, 1, 3, 0], getpos('.'))
+endfunc
+
+" 15) block select single numbers
+" Text:
+" 101
+" Expected:
+" 1) Ctrl-a on visually selected zero
+" 111
+func Test_visual_increment_15()
+ call setline(1, ["101"])
+ exec "norm! lv\<C-A>"
+ call assert_equal(["111"], getline(1, '$'))
+ call assert_equal([0, 1, 2, 0], getpos('.'))
+endfunc
+
+" 16) increment right aligned numbers
+" Text:
+" 1
+" 19
+" 119
+" Expected:
+" 1) Ctrl-a on line selected region
+" 2
+" 20
+" 120
+func Test_visual_increment_16()
+ call setline(1, [" 1", " 19", " 119"])
+ exec "norm! VG\<C-A>"
+ call assert_equal([" 2", " 20", " 120"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 17) block-wise increment and redo
+" Text:
+" 100
+" 1
+"
+" 100
+" 1
+"
+" Expected:
+" 1) Ctrl-V j $ on first block, afterwards '.' on second
+" 101
+" 2
+"
+" 101
+" 2
+func Test_visual_increment_17()
+ call setline(1, [" 100", " 1", "", " 100", " 1"])
+ exec "norm! \<C-V>j$\<C-A>2j."
+ call assert_equal([" 101", " 2", "", " 101", " 1"], getline(1, '$'))
+ call assert_equal([0, 3, 1, 0], getpos('.'))
+endfunc
+
+" 18) repeat of g<Ctrl-a>
+" Text:
+" 0
+" 0
+" 0
+" 0
+"
+" Expected:
+" 1) V 4j g<ctrl-a>, repeat twice afterwards with .
+" 3
+" 6
+" 9
+" 12
+func Test_visual_increment_18()
+ call setline(1, repeat(["0"], 4))
+ exec "norm! GV3kg\<C-A>"
+ exec "norm! .."
+ call assert_equal(["3", "6", "9", "12"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 19) increment on number with nrformat including alpha
+" Text:
+" 1
+" 1a
+"
+" Expected:
+" 1) <Ctrl-V>j$ <ctrl-a>
+" 2
+" 2a
+func Test_visual_increment_19()
+ set nrformats+=alpha
+ call setline(1, ["1", "1a"])
+ exec "norm! \<C-V>G$\<C-A>"
+ call assert_equal(["2", "2a"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 20) increment a single letter
+" Text:
+" a
+"
+" Expected:
+" 1) <Ctrl-a> and cursor is on a
+" b
+func Test_visual_increment_20()
+ set nrformats+=alpha
+ call setline(1, ["a"])
+ exec "norm! \<C-A>"
+ call assert_equal(["b"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 21) block-wise increment on part of hexadecimal
+" Text:
+" 0x123456
+"
+" Expected:
+" 1) Ctrl-V f3 <ctrl-a>
+" 0x124456
+func Test_visual_increment_21()
+ call setline(1, ["0x123456"])
+ exec "norm! \<C-V>f3\<C-A>"
+ call assert_equal(["0x124456"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 22) Block increment on 0b0
+" Text:
+" 0b1
+" 0b1
+" Expected:
+" 1) Ctrl-A on visually block selected region (cursor at beginning):
+" 0b10
+" 0b10
+" 2) Ctrl-A on visually block selected region (cursor at end)
+" 0b10
+" 0b10
+func Test_visual_increment_22()
+ call setline(1, repeat(["0b1"], 2))
+ exec "norm! \<C-V>j$\<C-A>"
+ call assert_equal(repeat(["0b10"], 2), getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+
+ call setline(1, repeat(["0b1"], 2))
+ exec "norm! $\<C-V>+\<C-A>"
+ call assert_equal(repeat(["0b10"], 2), getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 23) block-wise increment on part of binary
+" Text:
+" 0b1001
+"
+" Expected:
+" 1) Ctrl-V 5l <ctrl-a>
+" 0b1011
+func Test_visual_increment_23()
+ call setline(1, ["0b1001"])
+ exec "norm! \<C-V>4l\<C-A>"
+ call assert_equal(["0b1011"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 24) increment hexadecimal
+" Text:
+" 0x0b1001
+"
+" Expected:
+" 1) <ctrl-a>
+" 0x0b1002
+func Test_visual_increment_24()
+ call setline(1, ["0x0b1001"])
+ exec "norm! \<C-V>$\<C-A>"
+ call assert_equal(["0x0b1002"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 25) increment binary with nrformats including alpha
+" Text:
+" 0b1001a
+"
+" Expected:
+" 1) <ctrl-a>
+" 0b1010a
+func Test_visual_increment_25()
+ set nrformats+=alpha
+ call setline(1, ["0b1001a"])
+ exec "norm! \<C-V>$\<C-A>"
+ call assert_equal(["0b1010a"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" 26) increment binary with 32 bits
+" Text:
+" 0b11111111111111111111111111111110
+"
+" Expected:
+" 1) <ctrl-a>
+" 0b11111111111111111111111111111111
+func Test_visual_increment_26()
+ set nrformats+=alpha
+ call setline(1, ["0b11111111111111111111111111111110"])
+ exec "norm! \<C-V>$\<C-A>"
+ call assert_equal(["0b11111111111111111111111111111111"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+ set nrformats-=alpha
+endfunc
+
+" 27) increment with 'rightreft', if supported
+func Test_visual_increment_27()
+ if exists('+rightleft')
+ set rightleft
+ call setline(1, ["1234 56"])
+
+ exec "norm! $\<C-A>"
+ call assert_equal(["1234 57"], getline(1, '$'))
+ call assert_equal([0, 1, 7, 0], getpos('.'))
+
+ exec "norm! \<C-A>"
+ call assert_equal(["1234 58"], getline(1, '$'))
+ call assert_equal([0, 1, 7, 0], getpos('.'))
+ set norightleft
+ endif
+endfunc
+
+" Tab code and linewise-visual inc/dec
+func Test_visual_increment_28()
+ call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+ exec "norm! Vj\<C-A>"
+ call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+
+ call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+ exec "norm! ggVj\<C-X>"
+ call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" Tab code and linewise-visual inc/dec with 'nrformats'+=alpha
+func Test_visual_increment_29()
+ set nrformats+=alpha
+ call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+ exec "norm! Vj\<C-A>"
+ call assert_equal(["y\<TAB>10", "\<TAB>0"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+
+ call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+ exec "norm! ggVj\<C-X>"
+ call assert_equal(["w\<TAB>10", "\<TAB>-2"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" Tab code and character-visual inc/dec
+func Test_visual_increment_30()
+ call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+ exec "norm! f1vjf1\<C-A>"
+ call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$'))
+ call assert_equal([0, 1, 3, 0], getpos('.'))
+
+ call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+ exec "norm! ggf1vjf1\<C-X>"
+ call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$'))
+ call assert_equal([0, 1, 3, 0], getpos('.'))
+endfunc
+
+" Tab code and blockwise-visual inc/dec
+func Test_visual_increment_31()
+ call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+ exec "norm! f1\<C-V>jl\<C-A>"
+ call assert_equal(["x\<TAB>11", "\<TAB>0"], getline(1, '$'))
+ call assert_equal([0, 1, 3, 0], getpos('.'))
+
+ call setline(1, ["x\<TAB>10", "\<TAB>-1"])
+ exec "norm! ggf1\<C-V>jl\<C-X>"
+ call assert_equal(["x\<TAB>9", "\<TAB>-2"], getline(1, '$'))
+ call assert_equal([0, 1, 3, 0], getpos('.'))
+endfunc
+
+" Tab code and blockwise-visual decrement with 'linebreak' and 'showbreak'
+func Test_visual_increment_32()
+ 28vnew dummy_31
+ set linebreak showbreak=+
+ call setline(1, ["x\<TAB>\<TAB>\<TAB>10", "\<TAB>\<TAB>\<TAB>\<TAB>-1"])
+ exec "norm! ggf0\<C-V>jg_\<C-X>"
+ call assert_equal(["x\<TAB>\<TAB>\<TAB>1-1", "\<TAB>\<TAB>\<TAB>\<TAB>-2"], getline(1, '$'))
+ call assert_equal([0, 1, 6, 0], getpos('.'))
+ bwipe!
+endfunc
+
+" Tab code and blockwise-visual increment with $
+func Test_visual_increment_33()
+ call setline(1, ["\<TAB>123", "456"])
+ exec "norm! gg0\<C-V>j$\<C-A>"
+ call assert_equal(["\<TAB>124", "457"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" Tab code and blockwise-visual increment and redo
+func Test_visual_increment_34()
+ call setline(1, ["\<TAB>123", " 456789"])
+ exec "norm! gg0\<C-V>j\<C-A>"
+ call assert_equal(["\<TAB>123", " 457789"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+
+ exec "norm! .."
+ call assert_equal(["\<TAB>123", " 459789"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" Tab code, spaces and character-visual increment and redo
+func Test_visual_increment_35()
+ call setline(1, ["\<TAB>123", " 123", "\<TAB>123", "\<TAB>123"])
+ exec "norm! ggvjf3\<C-A>..."
+ call assert_equal(["\<TAB>127", " 127", "\<TAB>123", "\<TAB>123"], getline(1, '$'))
+ call assert_equal([0, 1, 2, 0], getpos('.'))
+endfunc
+
+" Tab code, spaces and blockwise-visual increment and redo
+func Test_visual_increment_36()
+ call setline(1, [" 123", "\<TAB>456789"])
+ exec "norm! G0\<C-V>kl\<C-A>"
+ call assert_equal([" 123", "\<TAB>556789"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+
+ exec "norm! ..."
+ call assert_equal([" 123", "\<TAB>856789"], getline(1, '$'))
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+endfunc
+
+" block-wise increment and dot-repeat
+" Text:
+" 1 23
+" 4 56
+"
+" Expected:
+" 1) f2 Ctrl-V jl <ctrl-a>, repeat twice afterwards with .
+" 1 26
+" 4 59
+"
+" Try with and without indent.
+func Test_visual_increment_37()
+ call setline(1, [" 1 23", " 4 56"])
+ exec "norm! ggf2\<C-V>jl\<C-A>.."
+ call assert_equal([" 1 26", " 4 59"], getline(1, 2))
+
+ call setline(1, ["1 23", "4 56"])
+ exec "norm! ggf2\<C-V>jl\<C-A>.."
+ call assert_equal(["1 26", "4 59"], getline(1, 2))
+endfunc
+
+" Check redo after the normal mode increment
+func Test_visual_increment_38()
+ exec "norm! i10\<ESC>5\<C-A>."
+ call assert_equal(["20"], getline(1, '$'))
+ call assert_equal([0, 1, 2, 0], getpos('.'))
+endfunc
+
+" Test what patch 7.3.414 fixed. Ctrl-A on "000" drops the leading zeros.
+func Test_normal_increment_01()
+ call setline(1, "000")
+ exec "norm! gg0\<C-A>"
+ call assert_equal("001", getline(1))
+
+ call setline(1, "000")
+ exec "norm! gg$\<C-A>"
+ call assert_equal("001", getline(1))
+
+ call setline(1, "001")
+ exec "norm! gg0\<C-A>"
+ call assert_equal("002", getline(1))
+
+ call setline(1, "001")
+ exec "norm! gg$\<C-A>"
+ call assert_equal("002", getline(1))
+endfunc
+
+" Test a regression of patch 7.4.1087 fixed.
+func Test_normal_increment_02()
+ call setline(1, ["hello 10", "world"])
+ exec "norm! ggl\<C-A>jx"
+ call assert_equal(["hello 11", "worl"], getline(1, '$'))
+ call assert_equal([0, 2, 4, 0], getpos('.'))
+endfunc
+
+" The test35 unified to this file.
+func Test_normal_increment_03()
+ call setline(1, ["100 0x100 077 0",
+ \ "100 0x100 077 ",
+ \ "100 0x100 077 0xfF 0xFf",
+ \ "100 0x100 077 "])
+ set nrformats=octal,hex
+ exec "norm! gg\<C-A>102\<C-X>\<C-A>l\<C-X>l\<C-A>64\<C-A>128\<C-X>$\<C-X>"
+ set nrformats=octal
+ exec "norm! j0\<C-A>102\<C-X>\<C-A>l\<C-X>2\<C-A>w65\<C-A>129\<C-X>blx6lD"
+ set nrformats=hex
+ exec "norm! j0101\<C-X>l257\<C-X>\<C-A>Txldt \<C-A> \<C-X> \<C-X>"
+ set nrformats=
+ exec "norm! j0200\<C-X>l100\<C-X>w78\<C-X>\<C-A>k"
+ call assert_equal(["0 0x0ff 0000 -1",
+ \ "0 1x100 0777777",
+ \ "-1 0x0 078 0xFE 0xfe",
+ \ "-100 -100x100 000 "], getline(1, '$'))
+ call assert_equal([0, 3, 25, 0], getpos('.'))
+endfunc
+
+
+" vim: tabstop=2 shiftwidth=2 expandtab
diff --git a/src/nvim/testdir/test_increment_dbcs.vim b/src/nvim/testdir/test_increment_dbcs.vim
new file mode 100644
index 0000000000..ee286a0a24
--- /dev/null
+++ b/src/nvim/testdir/test_increment_dbcs.vim
@@ -0,0 +1,29 @@
+" Tests for using Ctrl-A/Ctrl-X using DBCS.
+if !has('multi_byte')
+ finish
+endif
+scriptencoding cp932
+
+func SetUp()
+ new
+ set nrformats&
+endfunc
+
+func TearDown()
+ bwipe!
+endfunc
+
+func Test_increment_dbcs_1()
+ set nrformats+=alpha
+ call setline(1, ["R1"])
+ exec "norm! 0\<C-A>"
+ call assert_equal(["R2"], getline(1, '$'))
+ call assert_equal([0, 1, 4, 0], getpos('.'))
+
+ call setline(1, ["`ab0xDEe"])
+ exec "norm! 0\<C-X>"
+ call assert_equal(["`ab0xDDe"], getline(1, '$'))
+ call assert_equal([0, 1, 13, 0], getpos('.'))
+endfunc
+
+" vim: shiftwidth=2 expandtab
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 7adf6781b9..5e33597568 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -145,7 +145,7 @@ static int included_patches[] = {
// 2298 NA
// 2297 NA
// 2296,
- // 2295,
+ 2295,
2294,
// 2293,
// 2292,
@@ -161,11 +161,11 @@ static int included_patches[] = {
// 2282 NA
// 2281 NA
// 2280,
- // 2279,
+ 2279,
// 2278 NA
2277,
// 2276,
- // 2275,
+ 2275,
2274,
2273,
2272,
@@ -265,7 +265,7 @@ static int included_patches[] = {
// 2178,
// 2177,
// 2176 NA
- // 2175,
+ 2175,
2174,
// 2173,
// 2172,
@@ -281,7 +281,7 @@ static int included_patches[] = {
2162,
// 2161,
2160,
- // 2159,
+ 2159,
2158,
// 2157 NA
// 2156 NA
@@ -292,7 +292,7 @@ static int included_patches[] = {
// 2151,
// 2150 NA
2149,
- // 2148,
+ 2148,
2147,
2146,
// 2145 NA
@@ -352,13 +352,13 @@ static int included_patches[] = {
// 2091 NA
// 2090,
// 2089 NA
- // 2088,
- // 2087,
+ 2088,
+ 2087,
2086,
- // 2085,
- // 2084,
+ 2085,
+ 2084,
// 2083,
- // 2082,
+ 2082,
2081,
// 2080,
// 2079 NA
@@ -478,7 +478,7 @@ static int included_patches[] = {
// 1965 NA
1964,
// 1963 NA
- // 1962,
+ 1962,
1961,
1960,
// 1959 NA
@@ -486,13 +486,13 @@ static int included_patches[] = {
// 1957 NA
1956,
// 1955 NA
- // 1954,
+ 1954,
1953,
1952,
// 1951 NA
1950,
1949,
- // 1948,
+ 1948,
// 1947 NA
// 1946 NA
// 1945 NA
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 510f182353..28269e8889 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -4717,8 +4717,6 @@ void set_fraction(win_T *wp)
*/
void win_new_height(win_T *wp, int height)
{
- linenr_T lnum;
- int sline, line_size;
int prev_height = wp->w_height;
/* Don't want a negative height. Happens when splitting a tiny window.
@@ -4745,6 +4743,15 @@ void win_new_height(win_T *wp, int height)
wp->w_height = height;
wp->w_skipcol = 0;
+ scroll_to_fraction(wp, prev_height);
+}
+
+void scroll_to_fraction(win_T *wp, int prev_height)
+{
+ linenr_T lnum;
+ int sline, line_size;
+ int height = wp->w_height;
+
/* Don't change w_topline when height is zero. Don't set w_topline when
* 'scrollbind' is set and this isn't the current window. */
if (height > 0
diff --git a/test/functional/eval/printf_spec.lua b/test/functional/eval/printf_spec.lua
index c84290ceef..27e24c4118 100644
--- a/test/functional/eval/printf_spec.lua
+++ b/test/functional/eval/printf_spec.lua
@@ -1,7 +1,10 @@
local helpers = require('test.functional.helpers')(after_each)
+
local clear = helpers.clear
local eq = helpers.eq
+local eval = helpers.eval
local funcs = helpers.funcs
+local meths = helpers.meths
local exc_exec = helpers.exc_exec
describe('printf()', function()
@@ -57,4 +60,33 @@ describe('printf()', function()
it('errors out when %b modifier is used for a float', function()
eq('Vim(call):E805: Using a Float as a Number', exc_exec('call printf("%b", 3.1415926535)'))
end)
+ it('works with %p correctly', function()
+ local null_ret = nil
+ local seen_rets = {}
+ -- Collect all args in an array to avoid possible allocation of the same
+ -- address after freeing unreferenced values.
+ meths.set_var('__args', {})
+ local function check_printf(expr, is_null)
+ eq(0, exc_exec('call add(__args, ' .. expr .. ')'))
+ eq(0, exc_exec('let __result = printf("%p", __args[-1])'))
+ local id_ret = eval('id(__args[-1])')
+ eq(id_ret, meths.get_var('__result'))
+ if is_null then
+ if null_ret then
+ eq(null_ret, id_ret)
+ else
+ null_ret = id_ret
+ end
+ else
+ eq(nil, seen_rets[id_ret])
+ seen_rets[id_ret] = expr
+ end
+ meths.del_var('__result')
+ end
+ check_printf('v:_null_list', true)
+ check_printf('v:_null_dict', true)
+ check_printf('[]')
+ check_printf('{}')
+ check_printf('function("tr", ["a"])')
+ end)
end)
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index ca59c0dd2e..4db658d98c 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -17,7 +17,8 @@ local map = global_helpers.map
local filter = global_helpers.filter
local start_dir = lfs.currentdir()
-local nvim_prog = os.getenv('NVIM_PROG') or 'build/bin/nvim'
+-- XXX: NVIM_PROG takes precedence, QuickBuild sets it.
+local nvim_prog = os.getenv('NVIM_PROG') or os.getenv('NVIM_PRG') or 'build/bin/nvim'
local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N',
'--cmd', 'set shortmess+=I background=light noswapfile noautoindent laststatus=1 undodir=. directory=. viewdir=. backupdir=.',
'--embed'}