diff options
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 @@ -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'} |