aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/clint.py5
-rwxr-xr-xsrc/nvim/CMakeLists.txt50
-rw-r--r--src/nvim/api/buffer.c37
-rw-r--r--src/nvim/api/command.c7
-rw-r--r--src/nvim/api/keysets.lua1
-rw-r--r--src/nvim/api/options.c31
-rw-r--r--src/nvim/api/private/helpers.c2
-rw-r--r--src/nvim/api/vim.c10
-rw-r--r--src/nvim/arabic.c1066
-rw-r--r--src/nvim/arabic.h7
-rw-r--r--src/nvim/autocmd.c6
-rw-r--r--src/nvim/buffer.c255
-rw-r--r--src/nvim/buffer_defs.h35
-rw-r--r--src/nvim/change.c68
-rw-r--r--src/nvim/charset.c16
-rw-r--r--src/nvim/cursor_shape.c2
-rw-r--r--src/nvim/debugger.c2
-rw-r--r--src/nvim/diff.c6
-rw-r--r--src/nvim/digraph.c2
-rw-r--r--src/nvim/edit.c95
-rw-r--r--src/nvim/eval.c49
-rw-r--r--src/nvim/eval/funcs.c71
-rw-r--r--src/nvim/eval/typval.c4
-rw-r--r--src/nvim/eval/userfunc.c14
-rw-r--r--src/nvim/event/loop.c2
-rw-r--r--src/nvim/event/loop.h2
-rw-r--r--src/nvim/event/process.c2
-rw-r--r--src/nvim/ex_cmds.c97
-rw-r--r--src/nvim/ex_cmds.lua645
-rw-r--r--src/nvim/ex_cmds2.c54
-rw-r--r--src/nvim/ex_cmds2.h4
-rw-r--r--src/nvim/ex_cmds_defs.h7
-rw-r--r--src/nvim/ex_docmd.c230
-rw-r--r--src/nvim/ex_getln.c102
-rw-r--r--src/nvim/ex_getln.h5
-rw-r--r--src/nvim/ex_session.c30
-rw-r--r--src/nvim/file_search.c13
-rw-r--r--src/nvim/fileio.c97
-rw-r--r--src/nvim/fold.c533
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua4
-rw-r--r--src/nvim/getchar.c12
-rw-r--r--src/nvim/globals.h27
-rw-r--r--src/nvim/grid.c2
-rw-r--r--src/nvim/hardcopy.c5
-rw-r--r--src/nvim/hashtab.c4
-rw-r--r--src/nvim/indent.c11
-rw-r--r--src/nvim/indent_c.c14
-rw-r--r--src/nvim/lua/executor.c74
-rw-r--r--src/nvim/lua/stdlib.c25
-rw-r--r--src/nvim/main.c19
-rw-r--r--src/nvim/map.c5
-rw-r--r--src/nvim/map.h4
-rw-r--r--src/nvim/mapping.c18
-rw-r--r--src/nvim/mark.c592
-rw-r--r--src/nvim/mark.h17
-rw-r--r--src/nvim/mark_defs.h43
-rw-r--r--src/nvim/match.c10
-rw-r--r--src/nvim/mbyte.c5
-rw-r--r--src/nvim/memline.c36
-rw-r--r--src/nvim/memory.c1
-rw-r--r--src/nvim/menu.c326
-rw-r--r--src/nvim/menu.h1
-rw-r--r--src/nvim/message.c37
-rw-r--r--src/nvim/mouse.c147
-rw-r--r--src/nvim/move.c6
-rw-r--r--src/nvim/msgpack_rpc/channel.c11
-rw-r--r--src/nvim/normal.c1212
-rw-r--r--src/nvim/ops.c124
-rw-r--r--src/nvim/option.c101
-rw-r--r--src/nvim/option_defs.h11
-rw-r--r--src/nvim/options.lua12
-rw-r--r--src/nvim/os/env.c11
-rw-r--r--src/nvim/os/fs.c2
-rw-r--r--src/nvim/os/signal.c3
-rw-r--r--src/nvim/os/stdpaths.c4
-rw-r--r--src/nvim/path.c54
-rw-r--r--src/nvim/po/af.po11
-rw-r--r--src/nvim/po/de.po7
-rw-r--r--src/nvim/po/eo.po7
-rw-r--r--src/nvim/po/es.po7
-rw-r--r--src/nvim/po/fi.po8
-rw-r--r--src/nvim/po/fr.po6
-rw-r--r--src/nvim/po/ga.po15
-rw-r--r--src/nvim/po/it.po1
-rw-r--r--src/nvim/po/ja.euc-jp.po5
-rw-r--r--src/nvim/po/ja.po5
-rw-r--r--src/nvim/po/ru.po9
-rw-r--r--src/nvim/po/sr.po3
-rw-r--r--src/nvim/po/uk.po8
-rw-r--r--src/nvim/po/zh_CN.UTF-8.po6
-rw-r--r--src/nvim/po/zh_TW.UTF-8.po5
-rw-r--r--src/nvim/popupmnu.c190
-rw-r--r--src/nvim/quickfix.c17
-rw-r--r--src/nvim/regexp.c11
-rw-r--r--src/nvim/regexp_bt.c8
-rw-r--r--src/nvim/regexp_nfa.c9
-rw-r--r--src/nvim/runtime.c18
-rw-r--r--src/nvim/screen.c49
-rw-r--r--src/nvim/search.c65
-rw-r--r--src/nvim/shada.c21
-rw-r--r--src/nvim/sign.c21
-rw-r--r--src/nvim/spell.c40
-rw-r--r--src/nvim/spellfile.c51
-rw-r--r--src/nvim/syntax.c10
-rw-r--r--src/nvim/tag.c28
-rw-r--r--src/nvim/terminal.c2
-rw-r--r--src/nvim/testdir/check.vim16
-rw-r--r--src/nvim/testdir/runtest.vim16
-rw-r--r--src/nvim/testdir/setup.vim52
-rw-r--r--src/nvim/testdir/test_arabic.vim28
-rw-r--r--src/nvim/testdir/test_arglist.vim42
-rw-r--r--src/nvim/testdir/test_assert.vim40
-rw-r--r--src/nvim/testdir/test_autocmd.vim59
-rw-r--r--src/nvim/testdir/test_buffer.vim155
-rw-r--r--src/nvim/testdir/test_cd.vim15
-rw-r--r--src/nvim/testdir/test_changelist.vim2
-rw-r--r--src/nvim/testdir/test_charsearch.vim39
-rw-r--r--src/nvim/testdir/test_cindent.vim11
-rw-r--r--src/nvim/testdir/test_clientserver.vim8
-rw-r--r--src/nvim/testdir/test_cmdline.vim666
-rw-r--r--src/nvim/testdir/test_command_count.vim2
-rw-r--r--src/nvim/testdir/test_compiler.vim4
-rw-r--r--src/nvim/testdir/test_cscope.vim8
-rw-r--r--src/nvim/testdir/test_cursor_func.vim1
-rw-r--r--src/nvim/testdir/test_diffmode.vim4
-rw-r--r--src/nvim/testdir/test_digraph.vim10
-rw-r--r--src/nvim/testdir/test_edit.vim151
-rw-r--r--src/nvim/testdir/test_ex_mode.vim113
-rw-r--r--src/nvim/testdir/test_excmd.vim220
-rw-r--r--src/nvim/testdir/test_exists.vim4
-rw-r--r--src/nvim/testdir/test_expand.vim47
-rw-r--r--src/nvim/testdir/test_filechanged.vim18
-rw-r--r--src/nvim/testdir/test_filetype.vim143
-rw-r--r--src/nvim/testdir/test_filetype_lua.vim3
-rw-r--r--src/nvim/testdir/test_filter_cmd.vim8
-rw-r--r--src/nvim/testdir/test_findfile.vim6
-rw-r--r--src/nvim/testdir/test_float_func.vim5
-rw-r--r--src/nvim/testdir/test_functions.vim23
-rw-r--r--src/nvim/testdir/test_gf.vim52
-rw-r--r--src/nvim/testdir/test_global.vim12
-rw-r--r--src/nvim/testdir/test_gn.vim18
-rw-r--r--src/nvim/testdir/test_goto.vim59
-rw-r--r--src/nvim/testdir/test_help.vim5
-rw-r--r--src/nvim/testdir/test_help_tagjump.vim5
-rw-r--r--src/nvim/testdir/test_history.vim130
-rw-r--r--src/nvim/testdir/test_increment.vim8
-rw-r--r--src/nvim/testdir/test_indent.vim155
-rw-r--r--src/nvim/testdir/test_ins_complete.vim103
-rw-r--r--src/nvim/testdir/test_join.vim8
-rw-r--r--src/nvim/testdir/test_langmap.vim3
-rw-r--r--src/nvim/testdir/test_legacy_filetype.vim4
-rw-r--r--src/nvim/testdir/test_listlbr.vim6
-rw-r--r--src/nvim/testdir/test_listlbr_utf8.vim7
-rw-r--r--src/nvim/testdir/test_makeencoding.vim3
-rw-r--r--src/nvim/testdir/test_mapping.vim18
-rw-r--r--src/nvim/testdir/test_marks.vim36
-rw-r--r--src/nvim/testdir/test_matchadd_conceal_utf8.vim6
-rw-r--r--src/nvim/testdir/test_matchfuzzy.vim2
-rw-r--r--src/nvim/testdir/test_menu.vim94
-rw-r--r--src/nvim/testdir/test_messages.vim130
-rw-r--r--src/nvim/testdir/test_mksession.vim11
-rw-r--r--src/nvim/testdir/test_mksession_utf8.vim5
-rw-r--r--src/nvim/testdir/test_move.vim1
-rw-r--r--src/nvim/testdir/test_normal.vim819
-rw-r--r--src/nvim/testdir/test_options.vim119
-rw-r--r--src/nvim/testdir/test_perl.vim6
-rw-r--r--src/nvim/testdir/test_plus_arg_edit.vim15
-rw-r--r--src/nvim/testdir/test_popup.vim42
-rw-r--r--src/nvim/testdir/test_put.vim10
-rw-r--r--src/nvim/testdir/test_python2.vim5
-rw-r--r--src/nvim/testdir/test_python3.vim5
-rw-r--r--src/nvim/testdir/test_pyx2.vim5
-rw-r--r--src/nvim/testdir/test_pyx3.vim5
-rw-r--r--src/nvim/testdir/test_quickfix.vim230
-rw-r--r--src/nvim/testdir/test_quotestar.vim7
-rw-r--r--src/nvim/testdir/test_regexp_latin.vim142
-rw-r--r--src/nvim/testdir/test_registers.vim24
-rw-r--r--src/nvim/testdir/test_reltime.vim6
-rw-r--r--src/nvim/testdir/test_search.vim335
-rw-r--r--src/nvim/testdir/test_sha256.vim6
-rw-r--r--src/nvim/testdir/test_smartindent.vim66
-rw-r--r--src/nvim/testdir/test_sort.vim16
-rw-r--r--src/nvim/testdir/test_source.vim42
-rw-r--r--src/nvim/testdir/test_spell.vim144
-rw-r--r--src/nvim/testdir/test_spell_utf8.vim25
-rw-r--r--src/nvim/testdir/test_spellfile.vim745
-rw-r--r--src/nvim/testdir/test_statusline.vim18
-rw-r--r--src/nvim/testdir/test_substitute.vim89
-rw-r--r--src/nvim/testdir/test_swap.vim41
-rw-r--r--src/nvim/testdir/test_syntax.vim9
-rw-r--r--src/nvim/testdir/test_tabline.vim48
-rw-r--r--src/nvim/testdir/test_tabpage.vim79
-rw-r--r--src/nvim/testdir/test_tagjump.vim294
-rw-r--r--src/nvim/testdir/test_taglist.vim20
-rw-r--r--src/nvim/testdir/test_termcodes.vim34
-rw-r--r--src/nvim/testdir/test_textformat.vim125
-rw-r--r--src/nvim/testdir/test_textobjects.vim171
-rw-r--r--src/nvim/testdir/test_timers.vim110
-rw-r--r--src/nvim/testdir/test_trycatch.vim41
-rw-r--r--src/nvim/testdir/test_undo.vim145
-rw-r--r--src/nvim/testdir/test_usercommands.vim31
-rw-r--r--src/nvim/testdir/test_vartabs.vim5
-rw-r--r--src/nvim/testdir/test_vimscript.vim119
-rw-r--r--src/nvim/testdir/test_virtualedit.vim3
-rw-r--r--src/nvim/testdir/test_visual.vim121
-rw-r--r--src/nvim/testdir/test_window_cmd.vim366
-rw-r--r--src/nvim/testdir/test_writefile.vim2
-rw-r--r--src/nvim/testing.c72
-rw-r--r--src/nvim/tui/input.c163
-rw-r--r--src/nvim/tui/input.h1
-rw-r--r--src/nvim/tui/input_defs.h118
-rw-r--r--src/nvim/tui/tui.c79
-rw-r--r--src/nvim/undo.c21
-rw-r--r--src/nvim/version.c8
-rw-r--r--src/nvim/window.c53
215 files changed, 11190 insertions, 4112 deletions
diff --git a/src/clint.py b/src/clint.py
index 944946bd16..28f6031a57 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -651,6 +651,9 @@ def Error(filename, linenum, category, confidence, message):
elif _cpplint_state.output_format == 'eclipse':
sys.stdout.write('%s:%s: warning: %s [%s] [%d]\n' % (
filename, linenum, message, category, confidence))
+ elif _cpplint_state.output_format == 'gh_action':
+ sys.stdout.write('::error file=%s,line=%s::%s [%s] [%d]\n' % (
+ filename, linenum, message, category, confidence))
else:
sys.stdout.write('%s:%s: %s [%s] [%d]\n' % (
filename, linenum, message, category, confidence))
@@ -3053,7 +3056,7 @@ def ParseArguments(args):
if opt == '--help':
PrintUsage(None)
elif opt == '--output':
- if val not in ('emacs', 'vs7', 'eclipse'):
+ if val not in ('emacs', 'vs7', 'eclipse', 'gh_action'):
PrintUsage('The only allowed output formats are emacs,'
' vs7 and eclipse.')
output_format = val
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 77fd84bd73..384e672529 100755
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -221,31 +221,17 @@ function(get_preproc_output varname iname)
endif()
endfunction()
-# Handle generating version from Git.
-set(use_git_version 0)
-if(NVIM_VERSION_MEDIUM)
- message(STATUS "NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}")
-elseif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
- find_program(GIT_EXECUTABLE git)
- if(GIT_EXECUTABLE)
- message(STATUS "Using NVIM_VERSION_MEDIUM from Git")
- set(use_git_version 1)
- else()
- message(STATUS "Skipping version-string generation (cannot find git)")
- endif()
-endif()
-if(use_git_version)
- # Create a update_version_stamp target to update the version during build.
- file(RELATIVE_PATH relbuild "${PROJECT_SOURCE_DIR}" "${CMAKE_BINARY_DIR}")
- add_custom_target(update_version_stamp ALL
- COMMAND ${LUA_PRG} scripts/update_version_stamp.lua
- ${relbuild}/cmake.config/auto/versiondef_git.h
- "v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}${NVIM_VERSION_PRERELEASE}"
- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
- BYPRODUCTS ${CMAKE_BINARY_DIR}/cmake.config/auto/versiondef_git.h)
-else()
- file(WRITE ${CMAKE_BINARY_DIR}/cmake.config/auto/versiondef_git.h "")
-endif()
+set(NVIM_VERSION_GIT_H ${PROJECT_BINARY_DIR}/cmake.config/auto/versiondef_git.h)
+add_custom_target(update_version_stamp
+ COMMAND ${CMAKE_COMMAND}
+ -DNVIM_VERSION_MAJOR=${NVIM_VERSION_MAJOR}
+ -DNVIM_VERSION_MINOR=${NVIM_VERSION_MINOR}
+ -DNVIM_VERSION_PATCH=${NVIM_VERSION_PATCH}
+ -DNVIM_VERSION_PRERELEASE=${NVIM_VERSION_PRERELEASE}
+ -DOUTPUT=${NVIM_VERSION_GIT_H}
+ -P ${PROJECT_SOURCE_DIR}/cmake/GenerateVersion.cmake
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ BYPRODUCTS ${NVIM_VERSION_GIT_H})
# NVIM_GENERATED_FOR_HEADERS: generated headers to be included in headers
# NVIM_GENERATED_FOR_SOURCES: generated headers to be included in sources
@@ -279,9 +265,9 @@ foreach(sfile ${NVIM_SOURCES}
get_preproc_output(PREPROC_OUTPUT ${gf_i})
set(depends "${HEADER_GENERATOR}" "${sfile}")
- if(use_git_version AND "${f}" STREQUAL "version.c")
+ if("${f}" STREQUAL "version.c")
# Ensure auto/versiondef_git.h exists after "make clean".
- list(APPEND depends update_version_stamp)
+ list(APPEND depends "${NVIM_VERSION_GIT_H}")
endif()
add_custom_command(
OUTPUT "${gf_c_h}" "${gf_h_h}"
@@ -777,6 +763,12 @@ add_custom_command(
add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL} off)
+if(CI_BUILD)
+ set(LINT_OUTPUT_FORMAT gh_action)
+else()
+ set(LINT_OUTPUT_FORMAT vs7)
+endif()
+
set(LINT_NVIM_REL_SOURCES)
foreach(sfile ${LINT_NVIM_SOURCES})
get_test_target("" "${sfile}" r suffix)
@@ -786,7 +778,7 @@ foreach(sfile ${LINT_NVIM_SOURCES})
set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}")
add_custom_command(
OUTPUT ${touch_file}
- COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} ${rsfile}
+ COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} --output=${LINT_OUTPUT_FORMAT} ${rsfile}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
DEPENDS ${LINT_PRG} ${sfile} ${LINT_SUPPRESSES_TOUCH_FILE}
@@ -806,7 +798,7 @@ add_glob_targets(
add_custom_target(
lintcfull
COMMAND
- ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} ${LINT_NVIM_REL_SOURCES}
+ ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} --output=${LINT_OUTPUT_FORMAT} ${LINT_NVIM_REL_SOURCES}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${LINT_PRG} ${LINT_NVIM_SOURCES} ${LINT_SUPPRESS_FILE} lintuncrustify
)
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 1504004c6c..806b649ce6 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -1156,17 +1156,17 @@ Boolean nvim_buf_del_mark(Buffer buffer, String name, Error *err)
return res;
}
- pos_T *pos = getmark_buf(buf, *name.data, false);
+ fmark_T *fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, *name.data);
- // pos point to NULL when there's no mark with name
- if (pos == NULL) {
+ // fm is NULL when there's no mark with the given name
+ if (fm == NULL) {
api_set_error(err, kErrorTypeValidation, "Invalid mark name: '%c'",
*name.data);
return res;
}
- // pos->lnum is 0 when the mark is not valid in the buffer, or is not set.
- if (pos->lnum != 0) {
+ // mark.lnum is 0 when the mark is not valid in the buffer, or is not set.
+ if (fm->mark.lnum != 0 && fm->fnum == buf->handle) {
// since the mark belongs to the buffer delete it.
res = set_mark(buf, name, 0, 0, err);
}
@@ -1239,26 +1239,25 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
return rv;
}
- pos_T *posp;
+ fmark_T *fm;
+ pos_T pos;
char mark = *name.data;
- try_start();
- bufref_T save_buf;
- switch_buffer(&save_buf, buf);
- posp = getmark(mark, false);
- restore_buffer(&save_buf);
-
- if (try_end(err)) {
- return rv;
- }
-
- if (posp == NULL) {
+ fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, mark);
+ if (fm == NULL) {
api_set_error(err, kErrorTypeValidation, "Invalid mark name");
return rv;
}
+ // (0, 0) uppercase/file mark set in another buffer.
+ if (fm->fnum != buf->handle) {
+ pos.lnum = 0;
+ pos.col = 0;
+ } else {
+ pos = fm->mark;
+ }
- ADD(rv, INTEGER_OBJ(posp->lnum));
- ADD(rv, INTEGER_OBJ(posp->col));
+ ADD(rv, INTEGER_OBJ(pos.lnum));
+ ADD(rv, INTEGER_OBJ(pos.col));
return rv;
}
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index e6a055995e..4c2404a0d8 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -54,6 +54,7 @@
/// - force: (boolean) Whether filter is inverted or not.
/// - silent: (boolean) |:silent|.
/// - emsg_silent: (boolean) |:silent!|.
+/// - unsilent: (boolean) |:unsilent|.
/// - sandbox: (boolean) |:sandbox|.
/// - noautocmd: (boolean) |:noautocmd|.
/// - browse: (boolean) |:browse|.
@@ -232,6 +233,7 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
PUT(mods, "silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SILENT));
PUT(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_ERRSILENT));
+ PUT(mods, "unsilent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_UNSILENT));
PUT(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX));
PUT(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOAUTOCMD));
PUT(mods, "tab", INTEGER_OBJ(cmdinfo.cmdmod.cmod_tab));
@@ -598,6 +600,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
OBJ_TO_CMOD_FLAG(CMOD_SILENT, mods.silent, false, "'mods.silent'");
OBJ_TO_CMOD_FLAG(CMOD_ERRSILENT, mods.emsg_silent, false, "'mods.emsg_silent'");
+ OBJ_TO_CMOD_FLAG(CMOD_UNSILENT, mods.silent, false, "'mods.unsilent'");
OBJ_TO_CMOD_FLAG(CMOD_SANDBOX, mods.sandbox, false, "'mods.sandbox'");
OBJ_TO_CMOD_FLAG(CMOD_NOAUTOCMD, mods.noautocmd, false, "'mods.noautocmd'");
OBJ_TO_CMOD_FLAG(CMOD_BROWSE, mods.browse, false, "'mods.browse'");
@@ -722,6 +725,10 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
kv_concat(cmdline, "silent ");
}
+ if (cmdinfo->cmdmod.cmod_flags & CMOD_UNSILENT) {
+ kv_concat(cmdline, "unsilent ");
+ }
+
switch (cmdinfo->cmdmod.cmod_split & (WSP_ABOVE | WSP_BELOW | WSP_TOP | WSP_BOT)) {
case WSP_ABOVE:
kv_concat(cmdline, "aboveleft ");
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index 918fe028a8..21319fb7a6 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -181,6 +181,7 @@ return {
cmd_mods = {
"silent";
"emsg_silent";
+ "unsilent";
"filter";
"sandbox";
"noautocmd";
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 8c174fc129..4ed676e613 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -504,6 +504,7 @@ static int access_option_value(char *key, long *numval, char **stringval, int op
static int access_option_value_for(char *key, long *numval, char **stringval, int opt_flags,
int opt_type, void *from, bool get, Error *err)
{
+ bool need_switch = false;
switchwin_T switchwin;
aco_save_T aco;
int result = 0;
@@ -511,24 +512,32 @@ static int access_option_value_for(char *key, long *numval, char **stringval, in
try_start();
switch (opt_type) {
case SREQ_WIN:
- if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true)
- == FAIL) {
- restore_win_noblock(&switchwin, true);
- if (try_end(err)) {
+ need_switch = (win_T *)from != curwin;
+ if (need_switch) {
+ if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true)
+ == FAIL) {
+ restore_win_noblock(&switchwin, true);
+ if (try_end(err)) {
+ return result;
+ }
+ api_set_error(err, kErrorTypeException, "Problem while switching windows");
return result;
}
- api_set_error(err,
- kErrorTypeException,
- "Problem while switching windows");
- return result;
}
result = access_option_value(key, numval, stringval, opt_flags, get, err);
- restore_win_noblock(&switchwin, true);
+ if (need_switch) {
+ restore_win_noblock(&switchwin, true);
+ }
break;
case SREQ_BUF:
- aucmd_prepbuf(&aco, (buf_T *)from);
+ need_switch = (buf_T *)from != curbuf;
+ if (need_switch) {
+ aucmd_prepbuf(&aco, (buf_T *)from);
+ }
result = access_option_value(key, numval, stringval, opt_flags, get, err);
- aucmd_restbuf(&aco);
+ if (need_switch) {
+ aucmd_restbuf(&aco);
+ }
break;
case SREQ_GLOBAL:
result = access_option_value(key, numval, stringval, opt_flags, get, err);
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 436bcd5212..fad75d55be 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -916,7 +916,7 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err)
}
assert(INT32_MIN <= line && line <= INT32_MAX);
pos_T pos = { (linenr_T)line, (int)col, (int)col };
- res = setmark_pos(*name.data, &pos, buf->handle);
+ res = setmark_pos(*name.data, &pos, buf->handle, NULL);
if (!res) {
if (deleting) {
api_set_error(err, kErrorTypeException,
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index f91b74cd31..56516b2ac7 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -2027,20 +2027,20 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
return rv;
}
- xfmark_T mark = get_global_mark(*name.data);
- pos_T pos = mark.fmark.mark;
+ xfmark_T *mark = mark_get_global(false, *name.data); // false avoids loading the mark buffer
+ pos_T pos = mark->fmark.mark;
bool allocated = false;
int bufnr;
char *filename;
// Marks are from an open buffer it fnum is non zero
- if (mark.fmark.fnum != 0) {
- bufnr = mark.fmark.fnum;
+ if (mark->fmark.fnum != 0) {
+ bufnr = mark->fmark.fnum;
filename = (char *)buflist_nr2name(bufnr, true, true);
allocated = true;
// Marks comes from shada
} else {
- filename = mark.fname;
+ filename = mark->fname;
bufnr = 0;
}
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index 130ce65b86..06536e6e2b 100644
--- a/src/nvim/arabic.c
+++ b/src/nvim/arabic.c
@@ -5,6 +5,13 @@
///
/// Functions for Arabic language.
///
+/// Author: Nadim Shaikli & Isam Bayazidi
+/// Farsi support and restructuring to make adding new letters easier by Ali
+/// Gholami Rudi. Further work by Ameretat Reith.
+
+/// Sorted list of unicode Arabic characters. Each entry holds the
+/// presentation forms of a letter.
+///
/// Arabic characters are categorized into following types:
///
/// Isolated - iso-8859-6 form char denoted with a_*
@@ -19,12 +26,7 @@
#include "nvim/ascii.h"
#include "nvim/vim.h"
-// Arabic ISO-10646-1 character set definition
-
-// Arabic ISO-8859-6 (subset of 10646; 0600 - 06FF)
-#define a_COMMA 0x060C
-#define a_SEMICOLON 0x061B
-#define a_QUESTION 0x061F
+// Unicode values for Arabic characters.
#define a_HAMZA 0x0621
#define a_ALEF_MADDA 0x0622
#define a_ALEF_HAMZA_ABOVE 0x0623
@@ -62,7 +64,6 @@
#define a_WAW 0x0648
#define a_ALEF_MAKSURA 0x0649
#define a_YEH 0x064a
-
#define a_FATHATAN 0x064b
#define a_DAMMATAN 0x064c
#define a_KASRATAN 0x064d
@@ -71,168 +72,17 @@
#define a_KASRA 0x0650
#define a_SHADDA 0x0651
#define a_SUKUN 0x0652
-
#define a_MADDA_ABOVE 0x0653
#define a_HAMZA_ABOVE 0x0654
#define a_HAMZA_BELOW 0x0655
-#define a_ZERO 0x0660
-#define a_ONE 0x0661
-#define a_TWO 0x0662
-#define a_THREE 0x0663
-#define a_FOUR 0x0664
-#define a_FIVE 0x0665
-#define a_SIX 0x0666
-#define a_SEVEN 0x0667
-#define a_EIGHT 0x0668
-#define a_NINE 0x0669
-#define a_PERCENT 0x066a
-#define a_DECIMAL 0x066b
-#define a_THOUSANDS 0x066c
-#define a_STAR 0x066d
-#define a_MINI_ALEF 0x0670
-// Rest of 8859-6 does not relate to Arabic
+#define a_PEH 0x067e
+#define a_TCHEH 0x0686
+#define a_JEH 0x0698
+#define a_FKAF 0x06a9
+#define a_GAF 0x06af
+#define a_FYEH 0x06cc
-// Arabic Presentation Form-B (subset of 10646; FE70 - FEFF)
-//
-// s -> isolated
-// i -> initial
-// m -> medial
-// f -> final
-#define a_s_FATHATAN 0xfe70
-#define a_m_TATWEEL_FATHATAN 0xfe71
-#define a_s_DAMMATAN 0xfe72
-
-#define a_s_KASRATAN 0xfe74
-
-#define a_s_FATHA 0xfe76
-#define a_m_FATHA 0xfe77
-#define a_s_DAMMA 0xfe78
-#define a_m_DAMMA 0xfe79
-#define a_s_KASRA 0xfe7a
-#define a_m_KASRA 0xfe7b
-#define a_s_SHADDA 0xfe7c
-#define a_m_SHADDA 0xfe7d
-#define a_s_SUKUN 0xfe7e
-#define a_m_SUKUN 0xfe7f
-
-#define a_s_HAMZA 0xfe80
-#define a_s_ALEF_MADDA 0xfe81
-#define a_f_ALEF_MADDA 0xfe82
-#define a_s_ALEF_HAMZA_ABOVE 0xfe83
-#define a_f_ALEF_HAMZA_ABOVE 0xfe84
-#define a_s_WAW_HAMZA 0xfe85
-#define a_f_WAW_HAMZA 0xfe86
-#define a_s_ALEF_HAMZA_BELOW 0xfe87
-#define a_f_ALEF_HAMZA_BELOW 0xfe88
-#define a_s_YEH_HAMZA 0xfe89
-#define a_f_YEH_HAMZA 0xfe8a
-#define a_i_YEH_HAMZA 0xfe8b
-#define a_m_YEH_HAMZA 0xfe8c
-#define a_s_ALEF 0xfe8d
-#define a_f_ALEF 0xfe8e
-#define a_s_BEH 0xfe8f
-#define a_f_BEH 0xfe90
-#define a_i_BEH 0xfe91
-#define a_m_BEH 0xfe92
-#define a_s_TEH_MARBUTA 0xfe93
-#define a_f_TEH_MARBUTA 0xfe94
-#define a_s_TEH 0xfe95
-#define a_f_TEH 0xfe96
-#define a_i_TEH 0xfe97
-#define a_m_TEH 0xfe98
-#define a_s_THEH 0xfe99
-#define a_f_THEH 0xfe9a
-#define a_i_THEH 0xfe9b
-#define a_m_THEH 0xfe9c
-#define a_s_JEEM 0xfe9d
-#define a_f_JEEM 0xfe9e
-#define a_i_JEEM 0xfe9f
-#define a_m_JEEM 0xfea0
-#define a_s_HAH 0xfea1
-#define a_f_HAH 0xfea2
-#define a_i_HAH 0xfea3
-#define a_m_HAH 0xfea4
-#define a_s_KHAH 0xfea5
-#define a_f_KHAH 0xfea6
-#define a_i_KHAH 0xfea7
-#define a_m_KHAH 0xfea8
-#define a_s_DAL 0xfea9
-#define a_f_DAL 0xfeaa
-#define a_s_THAL 0xfeab
-#define a_f_THAL 0xfeac
-#define a_s_REH 0xfead
-#define a_f_REH 0xfeae
-#define a_s_ZAIN 0xfeaf
-#define a_f_ZAIN 0xfeb0
-#define a_s_SEEN 0xfeb1
-#define a_f_SEEN 0xfeb2
-#define a_i_SEEN 0xfeb3
-#define a_m_SEEN 0xfeb4
-#define a_s_SHEEN 0xfeb5
-#define a_f_SHEEN 0xfeb6
-#define a_i_SHEEN 0xfeb7
-#define a_m_SHEEN 0xfeb8
-#define a_s_SAD 0xfeb9
-#define a_f_SAD 0xfeba
-#define a_i_SAD 0xfebb
-#define a_m_SAD 0xfebc
-#define a_s_DAD 0xfebd
-#define a_f_DAD 0xfebe
-#define a_i_DAD 0xfebf
-#define a_m_DAD 0xfec0
-#define a_s_TAH 0xfec1
-#define a_f_TAH 0xfec2
-#define a_i_TAH 0xfec3
-#define a_m_TAH 0xfec4
-#define a_s_ZAH 0xfec5
-#define a_f_ZAH 0xfec6
-#define a_i_ZAH 0xfec7
-#define a_m_ZAH 0xfec8
-#define a_s_AIN 0xfec9
-#define a_f_AIN 0xfeca
-#define a_i_AIN 0xfecb
-#define a_m_AIN 0xfecc
-#define a_s_GHAIN 0xfecd
-#define a_f_GHAIN 0xfece
-#define a_i_GHAIN 0xfecf
-#define a_m_GHAIN 0xfed0
-#define a_s_FEH 0xfed1
-#define a_f_FEH 0xfed2
-#define a_i_FEH 0xfed3
-#define a_m_FEH 0xfed4
-#define a_s_QAF 0xfed5
-#define a_f_QAF 0xfed6
-#define a_i_QAF 0xfed7
-#define a_m_QAF 0xfed8
-#define a_s_KAF 0xfed9
-#define a_f_KAF 0xfeda
-#define a_i_KAF 0xfedb
-#define a_m_KAF 0xfedc
-#define a_s_LAM 0xfedd
-#define a_f_LAM 0xfede
-#define a_i_LAM 0xfedf
-#define a_m_LAM 0xfee0
-#define a_s_MEEM 0xfee1
-#define a_f_MEEM 0xfee2
-#define a_i_MEEM 0xfee3
-#define a_m_MEEM 0xfee4
-#define a_s_NOON 0xfee5
-#define a_f_NOON 0xfee6
-#define a_i_NOON 0xfee7
-#define a_m_NOON 0xfee8
-#define a_s_HEH 0xfee9
-#define a_f_HEH 0xfeea
-#define a_i_HEH 0xfeeb
-#define a_m_HEH 0xfeec
-#define a_s_WAW 0xfeed
-#define a_f_WAW 0xfeee
-#define a_s_ALEF_MAKSURA 0xfeef
-#define a_f_ALEF_MAKSURA 0xfef0
-#define a_s_YEH 0xfef1
-#define a_f_YEH 0xfef2
-#define a_i_YEH 0xfef3
-#define a_m_YEH 0xfef4
#define a_s_LAM_ALEF_MADDA_ABOVE 0xfef5
#define a_f_LAM_ALEF_MADDA_ABOVE 0xfef6
#define a_s_LAM_ALEF_HAMZA_ABOVE 0xfef7
@@ -242,664 +92,201 @@
#define a_s_LAM_ALEF 0xfefb
#define a_f_LAM_ALEF 0xfefc
+static struct achar {
+ unsigned c;
+ unsigned isolated;
+ unsigned initial;
+ unsigned medial;
+ unsigned final;
+} achars[] = {
+ { a_HAMZA, 0xfe80, 0, 0, 0 },
+ { a_ALEF_MADDA, 0xfe81, 0, 0, 0xfe82 },
+ { a_ALEF_HAMZA_ABOVE, 0xfe83, 0, 0, 0xfe84 },
+ { a_WAW_HAMZA, 0xfe85, 0, 0, 0xfe86 },
+ { a_ALEF_HAMZA_BELOW, 0xfe87, 0, 0, 0xfe88 },
+ { a_YEH_HAMZA, 0xfe89, 0xfe8b, 0xfe8c, 0xfe8a },
+ { a_ALEF, 0xfe8d, 0, 0, 0xfe8e },
+ { a_BEH, 0xfe8f, 0xfe91, 0xfe92, 0xfe90 },
+ { a_TEH_MARBUTA, 0xfe93, 0, 0, 0xfe94 },
+ { a_TEH, 0xfe95, 0xfe97, 0xfe98, 0xfe96 },
+ { a_THEH, 0xfe99, 0xfe9b, 0xfe9c, 0xfe9a },
+ { a_JEEM, 0xfe9d, 0xfe9f, 0xfea0, 0xfe9e },
+ { a_HAH, 0xfea1, 0xfea3, 0xfea4, 0xfea2 },
+ { a_KHAH, 0xfea5, 0xfea7, 0xfea8, 0xfea6 },
+ { a_DAL, 0xfea9, 0, 0, 0xfeaa },
+ { a_THAL, 0xfeab, 0, 0, 0xfeac },
+ { a_REH, 0xfead, 0, 0, 0xfeae },
+ { a_ZAIN, 0xfeaf, 0, 0, 0xfeb0 },
+ { a_SEEN, 0xfeb1, 0xfeb3, 0xfeb4, 0xfeb2 },
+ { a_SHEEN, 0xfeb5, 0xfeb7, 0xfeb8, 0xfeb6 },
+ { a_SAD, 0xfeb9, 0xfebb, 0xfebc, 0xfeba },
+ { a_DAD, 0xfebd, 0xfebf, 0xfec0, 0xfebe },
+ { a_TAH, 0xfec1, 0xfec3, 0xfec4, 0xfec2 },
+ { a_ZAH, 0xfec5, 0xfec7, 0xfec8, 0xfec6 },
+ { a_AIN, 0xfec9, 0xfecb, 0xfecc, 0xfeca },
+ { a_GHAIN, 0xfecd, 0xfecf, 0xfed0, 0xfece },
+ { a_TATWEEL, 0, 0x0640, 0x0640, 0x0640 },
+ { a_FEH, 0xfed1, 0xfed3, 0xfed4, 0xfed2 },
+ { a_QAF, 0xfed5, 0xfed7, 0xfed8, 0xfed6 },
+ { a_KAF, 0xfed9, 0xfedb, 0xfedc, 0xfeda },
+ { a_LAM, 0xfedd, 0xfedf, 0xfee0, 0xfede },
+ { a_MEEM, 0xfee1, 0xfee3, 0xfee4, 0xfee2 },
+ { a_NOON, 0xfee5, 0xfee7, 0xfee8, 0xfee6 },
+ { a_HEH, 0xfee9, 0xfeeb, 0xfeec, 0xfeea },
+ { a_WAW, 0xfeed, 0, 0, 0xfeee },
+ { a_ALEF_MAKSURA, 0xfeef, 0, 0, 0xfef0 },
+ { a_YEH, 0xfef1, 0xfef3, 0xfef4, 0xfef2 },
+ { a_FATHATAN, 0xfe70, 0, 0, 0 },
+ { a_DAMMATAN, 0xfe72, 0, 0, 0 },
+ { a_KASRATAN, 0xfe74, 0, 0, 0 },
+ { a_FATHA, 0xfe76, 0, 0xfe77, 0 },
+ { a_DAMMA, 0xfe78, 0, 0xfe79, 0 },
+ { a_KASRA, 0xfe7a, 0, 0xfe7b, 0 },
+ { a_SHADDA, 0xfe7c, 0, 0xfe7c, 0 },
+ { a_SUKUN, 0xfe7e, 0, 0xfe7f, 0 },
+ { a_MADDA_ABOVE, 0, 0, 0, 0 },
+ { a_HAMZA_ABOVE, 0, 0, 0, 0 },
+ { a_HAMZA_BELOW, 0, 0, 0, 0 },
+ { a_PEH, 0xfb56, 0xfb58, 0xfb59, 0xfb57 },
+ { a_TCHEH, 0xfb7a, 0xfb7c, 0xfb7d, 0xfb7b },
+ { a_JEH, 0xfb8a, 0, 0, 0xfb8b },
+ { a_FKAF, 0xfb8e, 0xfb90, 0xfb91, 0xfb8f },
+ { a_GAF, 0xfb92, 0xfb94, 0xfb95, 0xfb93 },
+ { a_FYEH, 0xfbfc, 0xfbfe, 0xfbff, 0xfbfd },
+};
+
#define a_BYTE_ORDER_MARK 0xfeff
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "arabic.c.generated.h"
#endif
-// Returns true if c is an ISO-8859-6 shaped ARABIC letter (user entered).
-static bool A_is_a(int cur_c)
-{
- switch (cur_c) {
- case a_HAMZA:
- case a_ALEF_MADDA:
- case a_ALEF_HAMZA_ABOVE:
- case a_WAW_HAMZA:
- case a_ALEF_HAMZA_BELOW:
- case a_YEH_HAMZA:
- case a_ALEF:
- case a_BEH:
- case a_TEH_MARBUTA:
- case a_TEH:
- case a_THEH:
- case a_JEEM:
- case a_HAH:
- case a_KHAH:
- case a_DAL:
- case a_THAL:
- case a_REH:
- case a_ZAIN:
- case a_SEEN:
- case a_SHEEN:
- case a_SAD:
- case a_DAD:
- case a_TAH:
- case a_ZAH:
- case a_AIN:
- case a_GHAIN:
- case a_TATWEEL:
- case a_FEH:
- case a_QAF:
- case a_KAF:
- case a_LAM:
- case a_MEEM:
- case a_NOON:
- case a_HEH:
- case a_WAW:
- case a_ALEF_MAKSURA:
- case a_YEH:
- return true;
- }
-
- return false;
-}
-
-// Returns true if c is an Isolated Form-B ARABIC letter
-static bool A_is_s(int cur_c)
+/// Find the struct achar pointer to the given Arabic char.
+/// Returns NULL if not found.
+static struct achar *find_achar(int c)
{
- switch (cur_c) {
- case a_s_HAMZA:
- case a_s_ALEF_MADDA:
- case a_s_ALEF_HAMZA_ABOVE:
- case a_s_WAW_HAMZA:
- case a_s_ALEF_HAMZA_BELOW:
- case a_s_YEH_HAMZA:
- case a_s_ALEF:
- case a_s_BEH:
- case a_s_TEH_MARBUTA:
- case a_s_TEH:
- case a_s_THEH:
- case a_s_JEEM:
- case a_s_HAH:
- case a_s_KHAH:
- case a_s_DAL:
- case a_s_THAL:
- case a_s_REH:
- case a_s_ZAIN:
- case a_s_SEEN:
- case a_s_SHEEN:
- case a_s_SAD:
- case a_s_DAD:
- case a_s_TAH:
- case a_s_ZAH:
- case a_s_AIN:
- case a_s_GHAIN:
- case a_s_FEH:
- case a_s_QAF:
- case a_s_KAF:
- case a_s_LAM:
- case a_s_MEEM:
- case a_s_NOON:
- case a_s_HEH:
- case a_s_WAW:
- case a_s_ALEF_MAKSURA:
- case a_s_YEH:
- return true;
+ // using binary search to find c
+ int h = ARRAY_SIZE(achars);
+ int l = 0;
+ while (l < h) {
+ int m = (h + l) / 2;
+ if (achars[m].c == (unsigned)c) {
+ return &achars[m];
+ }
+ if ((unsigned)c < achars[m].c) {
+ h = m;
+ } else {
+ l = m + 1;
+ }
}
-
- return false;
+ return NULL;
}
-// Returns true if c is a Final shape of an ARABIC letter
-static bool A_is_f(int cur_c)
+/// Change shape - from Combination (2 char) to an Isolated
+static int chg_c_laa2i(int hid_c)
{
- switch (cur_c) {
- case a_f_ALEF_MADDA:
- case a_f_ALEF_HAMZA_ABOVE:
- case a_f_WAW_HAMZA:
- case a_f_ALEF_HAMZA_BELOW:
- case a_f_YEH_HAMZA:
- case a_f_ALEF:
- case a_f_BEH:
- case a_f_TEH_MARBUTA:
- case a_f_TEH:
- case a_f_THEH:
- case a_f_JEEM:
- case a_f_HAH:
- case a_f_KHAH:
- case a_f_DAL:
- case a_f_THAL:
- case a_f_REH:
- case a_f_ZAIN:
- case a_f_SEEN:
- case a_f_SHEEN:
- case a_f_SAD:
- case a_f_DAD:
- case a_f_TAH:
- case a_f_ZAH:
- case a_f_AIN:
- case a_f_GHAIN:
- case a_f_FEH:
- case a_f_QAF:
- case a_f_KAF:
- case a_f_LAM:
- case a_f_MEEM:
- case a_f_NOON:
- case a_f_HEH:
- case a_f_WAW:
- case a_f_ALEF_MAKSURA:
- case a_f_YEH:
- case a_f_LAM_ALEF_MADDA_ABOVE:
- case a_f_LAM_ALEF_HAMZA_ABOVE:
- case a_f_LAM_ALEF_HAMZA_BELOW:
- case a_f_LAM_ALEF:
- return true;
- }
- return false;
-}
+ int tempc;
-// Change shape - from ISO-8859-6/Isolated to Form-B Isolated
-static int chg_c_a2s(int cur_c)
-{
- switch (cur_c) {
- case a_HAMZA:
- return a_s_HAMZA;
+ switch (hid_c) {
case a_ALEF_MADDA:
- return a_s_ALEF_MADDA;
+ tempc = a_s_LAM_ALEF_MADDA_ABOVE;
+ break;
case a_ALEF_HAMZA_ABOVE:
- return a_s_ALEF_HAMZA_ABOVE;
- case a_WAW_HAMZA:
- return a_s_WAW_HAMZA;
+ tempc = a_s_LAM_ALEF_HAMZA_ABOVE;
+ break;
case a_ALEF_HAMZA_BELOW:
- return a_s_ALEF_HAMZA_BELOW;
- case a_YEH_HAMZA:
- return a_s_YEH_HAMZA;
+ tempc = a_s_LAM_ALEF_HAMZA_BELOW;
+ break;
case a_ALEF:
- return a_s_ALEF;
- case a_TEH_MARBUTA:
- return a_s_TEH_MARBUTA;
- case a_DAL:
- return a_s_DAL;
- case a_THAL:
- return a_s_THAL;
- case a_REH:
- return a_s_REH;
- case a_ZAIN:
- return a_s_ZAIN;
- case a_TATWEEL:
- return cur_c; // exceptions
- case a_WAW:
- return a_s_WAW;
- case a_ALEF_MAKSURA:
- return a_s_ALEF_MAKSURA;
- case a_BEH:
- return a_s_BEH;
- case a_TEH:
- return a_s_TEH;
- case a_THEH:
- return a_s_THEH;
- case a_JEEM:
- return a_s_JEEM;
- case a_HAH:
- return a_s_HAH;
- case a_KHAH:
- return a_s_KHAH;
- case a_SEEN:
- return a_s_SEEN;
- case a_SHEEN:
- return a_s_SHEEN;
- case a_SAD:
- return a_s_SAD;
- case a_DAD:
- return a_s_DAD;
- case a_TAH:
- return a_s_TAH;
- case a_ZAH:
- return a_s_ZAH;
- case a_AIN:
- return a_s_AIN;
- case a_GHAIN:
- return a_s_GHAIN;
- case a_FEH:
- return a_s_FEH;
- case a_QAF:
- return a_s_QAF;
- case a_KAF:
- return a_s_KAF;
- case a_LAM:
- return a_s_LAM;
- case a_MEEM:
- return a_s_MEEM;
- case a_NOON:
- return a_s_NOON;
- case a_HEH:
- return a_s_HEH;
- case a_YEH:
- return a_s_YEH;
+ tempc = a_s_LAM_ALEF;
+ break;
+ default:
+ tempc = 0;
}
- return 0;
-}
-// Change shape - from ISO-8859-6/Isolated to Initial
-static int chg_c_a2i(int cur_c)
-{
- switch (cur_c) {
- case a_YEH_HAMZA:
- return a_i_YEH_HAMZA;
- case a_HAMZA:
- return a_s_HAMZA; // exceptions
- case a_ALEF_MADDA:
- return a_s_ALEF_MADDA; // exceptions
- case a_ALEF_HAMZA_ABOVE:
- return a_s_ALEF_HAMZA_ABOVE; // exceptions
- case a_WAW_HAMZA:
- return a_s_WAW_HAMZA; // exceptions
- case a_ALEF_HAMZA_BELOW:
- return a_s_ALEF_HAMZA_BELOW; // exceptions
- case a_ALEF:
- return a_s_ALEF; // exceptions
- case a_TEH_MARBUTA:
- return a_s_TEH_MARBUTA; // exceptions
- case a_DAL:
- return a_s_DAL; // exceptions
- case a_THAL:
- return a_s_THAL; // exceptions
- case a_REH:
- return a_s_REH; // exceptions
- case a_ZAIN:
- return a_s_ZAIN; // exceptions
- case a_TATWEEL:
- return cur_c; // exceptions
- case a_WAW:
- return a_s_WAW; // exceptions
- case a_ALEF_MAKSURA:
- return a_s_ALEF_MAKSURA; // exceptions
- case a_BEH:
- return a_i_BEH;
- case a_TEH:
- return a_i_TEH;
- case a_THEH:
- return a_i_THEH;
- case a_JEEM:
- return a_i_JEEM;
- case a_HAH:
- return a_i_HAH;
- case a_KHAH:
- return a_i_KHAH;
- case a_SEEN:
- return a_i_SEEN;
- case a_SHEEN:
- return a_i_SHEEN;
- case a_SAD:
- return a_i_SAD;
- case a_DAD:
- return a_i_DAD;
- case a_TAH:
- return a_i_TAH;
- case a_ZAH:
- return a_i_ZAH;
- case a_AIN:
- return a_i_AIN;
- case a_GHAIN:
- return a_i_GHAIN;
- case a_FEH:
- return a_i_FEH;
- case a_QAF:
- return a_i_QAF;
- case a_KAF:
- return a_i_KAF;
- case a_LAM:
- return a_i_LAM;
- case a_MEEM:
- return a_i_MEEM;
- case a_NOON:
- return a_i_NOON;
- case a_HEH:
- return a_i_HEH;
- case a_YEH:
- return a_i_YEH;
- }
- return 0;
+ return tempc;
}
-// Change shape - from ISO-8859-6/Isolated to Medial
-static int chg_c_a2m(int cur_c)
+/// Change shape - from Combination-Isolated to Final
+static int chg_c_laa2f(int hid_c)
{
- switch (cur_c) {
- case a_HAMZA:
- return a_s_HAMZA; // exception
+ int tempc;
+
+ switch (hid_c) {
case a_ALEF_MADDA:
- return a_f_ALEF_MADDA; // exception
+ tempc = a_f_LAM_ALEF_MADDA_ABOVE;
+ break;
case a_ALEF_HAMZA_ABOVE:
- return a_f_ALEF_HAMZA_ABOVE; // exception
- case a_WAW_HAMZA:
- return a_f_WAW_HAMZA; // exception
+ tempc = a_f_LAM_ALEF_HAMZA_ABOVE;
+ break;
case a_ALEF_HAMZA_BELOW:
- return a_f_ALEF_HAMZA_BELOW; // exception
- case a_YEH_HAMZA:
- return a_m_YEH_HAMZA;
+ tempc = a_f_LAM_ALEF_HAMZA_BELOW;
+ break;
case a_ALEF:
- return a_f_ALEF; // exception
- case a_BEH:
- return a_m_BEH;
- case a_TEH_MARBUTA:
- return a_f_TEH_MARBUTA; // exception
- case a_TEH:
- return a_m_TEH;
- case a_THEH:
- return a_m_THEH;
- case a_JEEM:
- return a_m_JEEM;
- case a_HAH:
- return a_m_HAH;
- case a_KHAH:
- return a_m_KHAH;
- case a_DAL:
- return a_f_DAL; // exception
- case a_THAL:
- return a_f_THAL; // exception
- case a_REH:
- return a_f_REH; // exception
- case a_ZAIN:
- return a_f_ZAIN; // exception
- case a_SEEN:
- return a_m_SEEN;
- case a_SHEEN:
- return a_m_SHEEN;
- case a_SAD:
- return a_m_SAD;
- case a_DAD:
- return a_m_DAD;
- case a_TAH:
- return a_m_TAH;
- case a_ZAH:
- return a_m_ZAH;
- case a_AIN:
- return a_m_AIN;
- case a_GHAIN:
- return a_m_GHAIN;
- case a_TATWEEL:
- return cur_c; // exception
- case a_FEH:
- return a_m_FEH;
- case a_QAF:
- return a_m_QAF;
- case a_KAF:
- return a_m_KAF;
- case a_LAM:
- return a_m_LAM;
- case a_MEEM:
- return a_m_MEEM;
- case a_NOON:
- return a_m_NOON;
- case a_HEH:
- return a_m_HEH;
- case a_WAW:
- return a_f_WAW; // exception
- case a_ALEF_MAKSURA:
- return a_f_ALEF_MAKSURA; // exception
- case a_YEH:
- return a_m_YEH;
+ tempc = a_f_LAM_ALEF;
+ break;
+ default:
+ tempc = 0;
}
- return 0;
+
+ return tempc;
}
-// Change shape - from ISO-8859-6/Isolated to final
-static int chg_c_a2f(int cur_c)
+/// Returns whether it is possible to join the given letters
+static int can_join(int c1, int c2)
{
- // NOTE: these encodings need to be accounted for
- //
- // a_f_ALEF_MADDA;
- // a_f_ALEF_HAMZA_ABOVE;
- // a_f_ALEF_HAMZA_BELOW;
- // a_f_LAM_ALEF_MADDA_ABOVE;
- // a_f_LAM_ALEF_HAMZA_ABOVE;
- // a_f_LAM_ALEF_HAMZA_BELOW;
+ struct achar *a1 = find_achar(c1);
+ struct achar *a2 = find_achar(c2);
- switch (cur_c) {
- case a_HAMZA:
- return a_s_HAMZA; // exception
- case a_ALEF_MADDA:
- return a_f_ALEF_MADDA;
- case a_ALEF_HAMZA_ABOVE:
- return a_f_ALEF_HAMZA_ABOVE;
- case a_WAW_HAMZA:
- return a_f_WAW_HAMZA;
- case a_ALEF_HAMZA_BELOW:
- return a_f_ALEF_HAMZA_BELOW;
- case a_YEH_HAMZA:
- return a_f_YEH_HAMZA;
- case a_ALEF:
- return a_f_ALEF;
- case a_BEH:
- return a_f_BEH;
- case a_TEH_MARBUTA:
- return a_f_TEH_MARBUTA;
- case a_TEH:
- return a_f_TEH;
- case a_THEH:
- return a_f_THEH;
- case a_JEEM:
- return a_f_JEEM;
- case a_HAH:
- return a_f_HAH;
- case a_KHAH:
- return a_f_KHAH;
- case a_DAL:
- return a_f_DAL;
- case a_THAL:
- return a_f_THAL;
- case a_REH:
- return a_f_REH;
- case a_ZAIN:
- return a_f_ZAIN;
- case a_SEEN:
- return a_f_SEEN;
- case a_SHEEN:
- return a_f_SHEEN;
- case a_SAD:
- return a_f_SAD;
- case a_DAD:
- return a_f_DAD;
- case a_TAH:
- return a_f_TAH;
- case a_ZAH:
- return a_f_ZAH;
- case a_AIN:
- return a_f_AIN;
- case a_GHAIN:
- return a_f_GHAIN;
- case a_TATWEEL:
- return cur_c; // exception
- case a_FEH:
- return a_f_FEH;
- case a_QAF:
- return a_f_QAF;
- case a_KAF:
- return a_f_KAF;
- case a_LAM:
- return a_f_LAM;
- case a_MEEM:
- return a_f_MEEM;
- case a_NOON:
- return a_f_NOON;
- case a_HEH:
- return a_f_HEH;
- case a_WAW:
- return a_f_WAW;
- case a_ALEF_MAKSURA:
- return a_f_ALEF_MAKSURA;
- case a_YEH:
- return a_f_YEH;
- }
- return 0;
+ return a1 && a2 && (a1->initial || a1->medial) && (a2->final || a2->medial);
}
-// Change shape - from Initial to Medial
-// This code is unreachable, because for the relevant characters ARABIC_CHAR()
-// is FALSE;
-#if 0
-static int chg_c_i2m(int cur_c)
+/// Check whether we are dealing with a character that could be regarded as an
+/// Arabic combining character, need to check the character before this.
+bool arabic_maycombine(int two)
+ FUNC_ATTR_PURE
{
- switch (cur_c) {
- case a_i_YEH_HAMZA:
- return a_m_YEH_HAMZA;
- case a_i_BEH:
- return a_m_BEH;
- case a_i_TEH:
- return a_m_TEH;
- case a_i_THEH:
- return a_m_THEH;
- case a_i_JEEM:
- return a_m_JEEM;
- case a_i_HAH:
- return a_m_HAH;
- case a_i_KHAH:
- return a_m_KHAH;
- case a_i_SEEN:
- return a_m_SEEN;
- case a_i_SHEEN:
- return a_m_SHEEN;
- case a_i_SAD:
- return a_m_SAD;
- case a_i_DAD:
- return a_m_DAD;
- case a_i_TAH:
- return a_m_TAH;
- case a_i_ZAH:
- return a_m_ZAH;
- case a_i_AIN:
- return a_m_AIN;
- case a_i_GHAIN:
- return a_m_GHAIN;
- case a_i_FEH:
- return a_m_FEH;
- case a_i_QAF:
- return a_m_QAF;
- case a_i_KAF:
- return a_m_KAF;
- case a_i_LAM:
- return a_m_LAM;
- case a_i_MEEM:
- return a_m_MEEM;
- case a_i_NOON:
- return a_m_NOON;
- case a_i_HEH:
- return a_m_HEH;
- case a_i_YEH:
- return a_m_YEH;
+ if (p_arshape && !p_tbidi) {
+ return two == a_ALEF_MADDA
+ || two == a_ALEF_HAMZA_ABOVE
+ || two == a_ALEF_HAMZA_BELOW
+ || two == a_ALEF;
}
- return 0;
+ return false;
}
-#endif
-// Change shape - from Final to Medial
-static int chg_c_f2m(int cur_c)
+/// Check whether we are dealing with Arabic combining characters.
+/// Note: these are NOT really composing characters!
+///
+/// @param one First character.
+/// @param two Character just after "one".
+bool arabic_combine(int one, int two)
+ FUNC_ATTR_PURE
{
- switch (cur_c) {
- // NOTE: these encodings are multi-positional, no ?
- // case a_f_ALEF_MADDA:
- // case a_f_ALEF_HAMZA_ABOVE:
- // case a_f_ALEF_HAMZA_BELOW:
- case a_f_YEH_HAMZA:
- return a_m_YEH_HAMZA;
- case a_f_WAW_HAMZA: // exceptions
- case a_f_ALEF:
- case a_f_TEH_MARBUTA:
- case a_f_DAL:
- case a_f_THAL:
- case a_f_REH:
- case a_f_ZAIN:
- case a_f_WAW:
- case a_f_ALEF_MAKSURA:
- return cur_c;
- case a_f_BEH:
- return a_m_BEH;
- case a_f_TEH:
- return a_m_TEH;
- case a_f_THEH:
- return a_m_THEH;
- case a_f_JEEM:
- return a_m_JEEM;
- case a_f_HAH:
- return a_m_HAH;
- case a_f_KHAH:
- return a_m_KHAH;
- case a_f_SEEN:
- return a_m_SEEN;
- case a_f_SHEEN:
- return a_m_SHEEN;
- case a_f_SAD:
- return a_m_SAD;
- case a_f_DAD:
- return a_m_DAD;
- case a_f_TAH:
- return a_m_TAH;
- case a_f_ZAH:
- return a_m_ZAH;
- case a_f_AIN:
- return a_m_AIN;
- case a_f_GHAIN:
- return a_m_GHAIN;
- case a_f_FEH:
- return a_m_FEH;
- case a_f_QAF:
- return a_m_QAF;
- case a_f_KAF:
- return a_m_KAF;
- case a_f_LAM:
- return a_m_LAM;
- case a_f_MEEM:
- return a_m_MEEM;
- case a_f_NOON:
- return a_m_NOON;
- case a_f_HEH:
- return a_m_HEH;
- case a_f_YEH:
- return a_m_YEH;
- // NOTE: these encodings are multi-positional, no ?
- // case a_f_LAM_ALEF_MADDA_ABOVE:
- // case a_f_LAM_ALEF_HAMZA_ABOVE:
- // case a_f_LAM_ALEF_HAMZA_BELOW:
- // case a_f_LAM_ALEF:
+ if (one == a_LAM) {
+ return arabic_maycombine(two);
}
- return 0;
+ return false;
}
-// Change shape - from Combination (2 char) to an Isolated.
-static int chg_c_laa2i(int hid_c)
+/// A_is_iso returns true if 'c' is an Arabic ISO-8859-6 character
+/// (alphabet/number/punctuation)
+static int A_is_iso(int c)
{
- switch (hid_c) {
- case a_ALEF_MADDA:
- return a_s_LAM_ALEF_MADDA_ABOVE;
- case a_ALEF_HAMZA_ABOVE:
- return a_s_LAM_ALEF_HAMZA_ABOVE;
- case a_ALEF_HAMZA_BELOW:
- return a_s_LAM_ALEF_HAMZA_BELOW;
- case a_ALEF:
- return a_s_LAM_ALEF;
- }
- return 0;
+ return find_achar(c) != NULL;
}
-// Change shape - from Combination-Isolated to Final.
-static int chg_c_laa2f(int hid_c)
+/// A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B)
+static int A_is_ok(int c)
{
- switch (hid_c) {
- case a_ALEF_MADDA:
- return a_f_LAM_ALEF_MADDA_ABOVE;
- case a_ALEF_HAMZA_ABOVE:
- return a_f_LAM_ALEF_HAMZA_ABOVE;
- case a_ALEF_HAMZA_BELOW:
- return a_f_LAM_ALEF_HAMZA_BELOW;
- case a_ALEF:
- return a_f_LAM_ALEF;
- }
- return 0;
+ return (A_is_iso(c) || c == a_BYTE_ORDER_MARK);
}
-// Do "half-shaping" on character "c". Return zero if no shaping.
-static int half_shape(int c)
+/// A_is_valid returns true if 'c' is an Arabic 10646 (8859-6 or Form-B)
+/// with some exceptions/exclusions
+static int A_is_valid(int c)
{
- if (A_is_a(c)) {
- return chg_c_a2i(c);
- }
-
- if (A_is_valid(c) && A_is_f(c)) {
- return chg_c_f2m(c);
- }
- return 0;
+ return (A_is_ok(c) && c != a_HAMZA);
}
// Do Arabic shaping on character "c". Returns the shaped character.
@@ -916,37 +303,35 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int next_c)
return c;
}
- // half-shape current and previous character
- int shape_c = half_shape(prev_c);
-
int curr_c;
- int curr_laa = A_firstc_laa(c, *c1p);
- int prev_laa = A_firstc_laa(prev_c, prev_c1);
+ int curr_laa = arabic_combine(c, *c1p);
+ int prev_laa = arabic_combine(prev_c, prev_c1);
if (curr_laa) {
- if (A_is_valid(prev_c) && !A_is_f(shape_c) && !A_is_s(shape_c)
- && !prev_laa) {
- curr_c = chg_c_laa2f(curr_laa);
+ if (A_is_valid(prev_c) && can_join(prev_c, a_LAM) && !prev_laa) {
+ curr_c = chg_c_laa2f(*c1p);
} else {
- curr_c = chg_c_laa2i(curr_laa);
+ curr_c = chg_c_laa2i(*c1p);
}
-
// Remove the composing character
*c1p = 0;
- } else if (!A_is_valid(prev_c) && A_is_valid(next_c)) {
- curr_c = chg_c_a2i(c);
- } else if (!shape_c || A_is_f(shape_c) || A_is_s(shape_c) || prev_laa) {
- curr_c = A_is_valid(next_c) ? chg_c_a2i(c) : chg_c_a2s(c);
- } else if (A_is_valid(next_c)) {
-#if 0
- curr_c = A_is_iso(c) ? chg_c_a2m(c) : chg_c_i2m(c);
-#else
- curr_c = A_is_iso(c) ? chg_c_a2m(c) : 0;
-#endif
- } else if (A_is_valid(prev_c)) {
- curr_c = chg_c_a2f(c);
} else {
- curr_c = chg_c_a2s(c);
+ struct achar *curr_a = find_achar(c);
+ int backward_combine = !prev_laa && can_join(prev_c, c);
+ int forward_combine = can_join(c, next_c);
+
+ if (backward_combine && forward_combine) {
+ curr_c = (int)curr_a->medial;
+ }
+ if (backward_combine && !forward_combine) {
+ curr_c = (int)curr_a->final;
+ }
+ if (!backward_combine && forward_combine) {
+ curr_c = (int)curr_a->initial;
+ }
+ if (!backward_combine && !forward_combine) {
+ curr_c = (int)curr_a->isolated;
+ }
}
// Sanity check -- curr_c should, in the future, never be 0.
@@ -966,88 +351,3 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int next_c)
// Return the shaped character
return curr_c;
}
-
-/// Check whether we are dealing with Arabic combining characters.
-/// Note: these are NOT really composing characters!
-///
-/// @param one First character.
-/// @param two Character just after "one".
-bool arabic_combine(int one, int two)
- FUNC_ATTR_PURE
-{
- if (one == a_LAM) {
- return arabic_maycombine(two);
- }
- return false;
-}
-
-/// Check whether we are dealing with a character that could be regarded as an
-/// Arabic combining character, need to check the character before this.
-bool arabic_maycombine(int two)
- FUNC_ATTR_PURE
-{
- if (p_arshape && !p_tbidi) {
- return two == a_ALEF_MADDA
- || two == a_ALEF_HAMZA_ABOVE
- || two == a_ALEF_HAMZA_BELOW
- || two == a_ALEF;
- }
- return false;
-}
-
-// A_firstc_laa returns first character of LAA combination if it exists
-// in: "c" base character
-// in: "c1" first composing character
-static int A_firstc_laa(int c, int c1)
-{
- if ((c1 != NUL) && (c == a_LAM) && !A_is_harakat(c1)) {
- return c1;
- }
- return 0;
-}
-
-// A_is_harakat returns true if 'c' is an Arabic Harakat character.
-// (harakat/tanween)
-static bool A_is_harakat(int c)
-{
- return c >= a_FATHATAN && c <= a_SUKUN;
-}
-
-// A_is_iso returns true if 'c' is an Arabic ISO-8859-6 character.
-// (alphabet/number/punctuation)
-static bool A_is_iso(int c)
-{
- return ((c >= a_HAMZA && c <= a_GHAIN)
- || (c >= a_TATWEEL && c <= a_HAMZA_BELOW)
- || c == a_MINI_ALEF);
-}
-
-// A_is_formb returns true if 'c' is an Arabic 10646-1 FormB character.
-// (alphabet/number/punctuation)
-static bool A_is_formb(int c)
-{
- return ((c >= a_s_FATHATAN && c <= a_s_DAMMATAN)
- || c == a_s_KASRATAN
- || (c >= a_s_FATHA && c <= a_f_LAM_ALEF)
- || c == a_BYTE_ORDER_MARK);
-}
-
-// A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B).
-static bool A_is_ok(int c)
-{
- return A_is_iso(c) || A_is_formb(c);
-}
-
-// A_is_valid returns true if 'c' is an Arabic 10646 (8859-6 or Form-B),
-// with some exceptions/exclusions.
-static bool A_is_valid(int c)
-{
- return A_is_ok(c) && !A_is_special(c);
-}
-
-// A_is_special returns true if 'c' is not a special Arabic character.
-// Specials don't adhere to most of the rules.
-static bool A_is_special(int c)
-{
- return c == a_HAMZA || c == a_s_HAMZA;
-}
diff --git a/src/nvim/arabic.h b/src/nvim/arabic.h
index eaab463777..3c34de1449 100644
--- a/src/nvim/arabic.h
+++ b/src/nvim/arabic.h
@@ -3,12 +3,7 @@
#include <stdbool.h>
-/// Whether c belongs to the range of Arabic characters that might be shaped.
-static inline bool arabic_char(int c)
-{
- // return c >= a_HAMZA && c <= a_MINI_ALEF;
- return c >= 0x0621 && c <= 0x0670;
-}
+#define ARABIC_CHAR(ch) (((ch) & 0xFF00) == 0x0600)
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "arabic.h.generated.h"
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 75666c600c..d51079b515 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -1679,7 +1679,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
} else if (fname != NULL && !ends_excmd(*fname)) {
autocmd_fname = fname;
} else if (buf != NULL) {
- autocmd_fname = (char *)buf->b_ffname;
+ autocmd_fname = buf->b_ffname;
} else {
autocmd_fname = NULL;
}
@@ -1711,9 +1711,9 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
fname = (char *)buf->b_p_ft;
} else {
if (buf->b_sfname != NULL) {
- sfname = (char *)vim_strsave(buf->b_sfname);
+ sfname = xstrdup(buf->b_sfname);
}
- fname = (char *)buf->b_ffname;
+ fname = buf->b_ffname;
}
}
if (fname == NULL) {
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index baecda8e3c..f937450107 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -55,6 +55,7 @@
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
+#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -88,15 +89,24 @@
static char *msg_loclist = N_("[Location List]");
static char *msg_qflist = N_("[Quickfix List]");
static char *e_auabort = N_("E855: Autocommands caused command to abort");
+static char *e_buflocked = N_("E937: Attempt to delete a buffer that is in use");
// Number of times free_buffer() was called.
static int buf_free_count = 0;
+static int top_file_num = 1; ///< highest file number
+
typedef enum {
kBffClearWinInfo = 1,
kBffInitChangedtick = 2,
} BufFreeFlags;
+/// @return the highest possible buffer number
+int get_highest_fnum(void)
+{
+ return top_file_num - 1;
+}
+
/// Read data from buffer for retrying.
///
/// @param read_stdin read file from stdin, otherwise fifo
@@ -112,7 +122,7 @@ static int read_buffer(int read_stdin, exarg_T *eap, int flags)
// the end. This makes it possible to retry when 'fileformat' or
// 'fileencoding' was guessed wrong.
line_count = curbuf->b_ml.ml_line_count;
- retval = readfile(read_stdin ? NULL : (char *)curbuf->b_ffname,
+ retval = readfile(read_stdin ? NULL : curbuf->b_ffname,
read_stdin ? NULL : curbuf->b_fname,
line_count, (linenr_T)0, (linenr_T)MAXLNUM, eap,
flags | READ_BUFFER, silent);
@@ -230,7 +240,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
}
#endif
- retval = readfile((char *)curbuf->b_ffname, curbuf->b_fname,
+ retval = readfile(curbuf->b_ffname, curbuf->b_fname,
(linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap,
flags | READ_NEW | (read_fifo ? READ_FIFO : 0), silent);
#ifdef UNIX
@@ -437,10 +447,11 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
// Disallow deleting the buffer when it is locked (already being closed or
// halfway a command that relies on it). Unloading is allowed.
if (buf->b_locked > 0 && (del_buf || wipe_buf)) {
- emsg(_("E937: Attempt to delete a buffer that is in use"));
+ emsg(_(e_buflocked));
return false;
}
+ // check no autocommands closed the window
if (win != NULL // Avoid bogus clang warning.
&& win_valid_any_tab(win)) {
// Set b_last_cursor when closing the last window for the buffer.
@@ -528,7 +539,9 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
}
if (buf->terminal) {
+ buf->b_locked++;
terminal_close(&buf->terminal, -1);
+ buf->b_locked--;
}
// Always remove the buffer when there is no file name.
@@ -802,8 +815,7 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
// Avoid losing b:changedtick when deleting buffer: clearing variables
// implies using clear_tv() on b:changedtick and that sets changedtick to
// zero.
- hashitem_T *const changedtick_hi = hash_find(&buf->b_vars->dv_hashtab,
- (const char_u *)"changedtick");
+ hashitem_T *const changedtick_hi = hash_find(&buf->b_vars->dv_hashtab, "changedtick");
assert(changedtick_hi != NULL);
hash_remove(&buf->b_vars->dv_hashtab, changedtick_hi);
}
@@ -813,7 +825,7 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
buf_init_changedtick(buf);
}
uc_clear(&buf->b_ucmds); // clear local user commands
- buf_delete_signs(buf, (char_u *)"*"); // delete any signs
+ buf_delete_signs(buf, "*"); // delete any signs
extmark_free_all(buf); // delete any extmarks
map_clear_mode(buf, MAP_ALL_MODES, true, false); // clear local mappings
map_clear_mode(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
@@ -942,7 +954,7 @@ void handle_swap_exists(bufref_T *old_curbuf)
/// @param end_bnr buffer nr or last buffer nr in a range
///
/// @return error message or NULL
-char *do_bufdel(int command, char_u *arg, int addr_count, int start_bnr, int end_bnr, int forceit)
+char *do_bufdel(int command, char *arg, int addr_count, int start_bnr, int end_bnr, int forceit)
{
int do_current = 0; // delete current buffer?
int deleted = 0; // number of buffers deleted
@@ -980,17 +992,17 @@ char *do_bufdel(int command, char_u *arg, int addr_count, int start_bnr, int end
break;
}
} else { // addr_count == 1
- arg = (char_u *)skipwhite((char *)arg);
+ arg = skipwhite(arg);
if (*arg == NUL) {
break;
}
if (!ascii_isdigit(*arg)) {
- p = (char *)skiptowhite_esc(arg);
- bnr = buflist_findpat(arg, (char_u *)p, command == DOBUF_WIPE, false, false);
+ p = skiptowhite_esc(arg);
+ bnr = buflist_findpat(arg, p, command == DOBUF_WIPE, false, false);
if (bnr < 0) { // failed
break;
}
- arg = (char_u *)p;
+ arg = p;
} else {
bnr = getdigits_int(&arg, false, 0);
}
@@ -1182,6 +1194,10 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
if (unload) {
int forward;
bufref_T bufref;
+ if (buf->b_locked) {
+ emsg(_(e_buflocked));
+ return FAIL;
+ }
set_bufref(&bufref, buf);
// When unloading or deleting a buffer that's already unloaded and
@@ -1636,8 +1652,6 @@ void no_write_message_nobang(const buf_T *const buf)
// functions for dealing with the buffer list
//
-static int top_file_num = 1; ///< highest file number
-
/// Initialize b:changedtick and changedtick_val attribute
///
/// @param[out] buf Buffer to initialize for.
@@ -1676,10 +1690,10 @@ static inline void buf_init_changedtick(buf_T *const buf)
/// @param bufnr
///
/// @return pointer to the buffer
-buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int flags)
+buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
{
- char *ffname = (char *)ffname_arg;
- char *sfname = (char *)sfname_arg;
+ char *ffname = ffname_arg;
+ char *sfname = sfname_arg;
buf_T *buf;
fname_expand(curbuf, &ffname, &sfname); // will allocate ffname
@@ -1746,8 +1760,8 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl
}
if (ffname != NULL) {
- buf->b_ffname = (char_u *)ffname;
- buf->b_sfname = vim_strsave((char_u *)sfname);
+ buf->b_ffname = ffname;
+ buf->b_sfname = xstrdup(sfname);
}
clear_wininfo(buf);
@@ -1789,13 +1803,14 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl
buf_copy_options(buf, BCO_ALWAYS);
}
- buf->b_wininfo->wi_fpos.lnum = lnum;
+ buf->b_wininfo->wi_mark = (fmark_T)INIT_FMARK;
+ buf->b_wininfo->wi_mark.mark.lnum = lnum;
buf->b_wininfo->wi_win = curwin;
hash_init(&buf->b_s.b_keywtab);
hash_init(&buf->b_s.b_keywtab_ic);
- buf->b_fname = (char *)buf->b_sfname;
+ buf->b_fname = buf->b_sfname;
if (!file_id_valid) {
buf->file_id_valid = false;
} else {
@@ -1937,7 +1952,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
{
buf_T *buf;
win_T *wp = NULL;
- pos_T *fpos;
+ fmark_T *fm = NULL;
colnr_T col;
buf = buflist_findnr(n);
@@ -1955,19 +1970,17 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
return OK;
}
- if (text_locked()) {
- text_locked_msg();
- return FAIL;
- }
- if (curbuf_locked()) {
+ if (text_or_buf_locked()) {
return FAIL;
}
+ bool restore_view = false;
// altfpos may be changed by getfile(), get it now
if (lnum == 0) {
- fpos = buflist_findfpos(buf);
- lnum = fpos->lnum;
- col = fpos->col;
+ fm = buflist_findfmark(buf);
+ lnum = fm->mark.lnum;
+ col = fm->mark.col;
+ restore_view = true;
} else {
col = 0;
}
@@ -2011,6 +2024,9 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
curwin->w_cursor.coladd = 0;
curwin->w_set_curswant = true;
}
+ if (jop_flags & JOP_VIEW && restore_view) {
+ mark_view_restore(fm);
+ }
return OK;
}
RedrawingDisabled--;
@@ -2022,7 +2038,7 @@ void buflist_getfpos(void)
{
pos_T *fpos;
- fpos = buflist_findfpos(curbuf);
+ fpos = &buflist_findfmark(curbuf)->mark;
curwin->w_cursor.lnum = fpos->lnum;
check_cursor_lnum();
@@ -2040,13 +2056,13 @@ void buflist_getfpos(void)
/// Find file in buffer list by name (it has to be for the current window).
///
/// @return buffer or NULL if not found
-buf_T *buflist_findname_exp(char_u *fname)
+buf_T *buflist_findname_exp(char *fname)
{
char *ffname;
buf_T *buf = NULL;
// First make the name into a full path name
- ffname = FullName_save((char *)fname,
+ ffname = FullName_save(fname,
#ifdef UNIX
// force expansion, get rid of symbolic links
true
@@ -2055,7 +2071,7 @@ buf_T *buflist_findname_exp(char_u *fname)
#endif
); // NOLINT(whitespace/parens)
if (ffname != NULL) {
- buf = buflist_findname((char_u *)ffname);
+ buf = buflist_findname(ffname);
xfree(ffname);
}
return buf;
@@ -2066,11 +2082,11 @@ buf_T *buflist_findname_exp(char_u *fname)
/// Skips dummy buffers.
///
/// @return buffer or NULL if not found
-buf_T *buflist_findname(char_u *ffname)
+buf_T *buflist_findname(char *ffname)
{
FileID file_id;
- bool file_id_valid = os_fileid((char *)ffname, &file_id);
- return buflist_findname_file_id((char *)ffname, &file_id, file_id_valid);
+ bool file_id_valid = os_fileid(ffname, &file_id);
+ return buflist_findname_file_id(ffname, &file_id, file_id_valid);
}
/// Same as buflist_findname(), but pass the FileID structure to avoid
@@ -2098,7 +2114,7 @@ static buf_T *buflist_findname_file_id(char *ffname, FileID *file_id, bool file_
/// @param curtab_only find buffers in current tab only
///
/// @return fnum of the found buffer or < 0 for error.
-int buflist_findpat(const char_u *pattern, const char_u *pattern_end, bool unlisted, bool diffmode,
+int buflist_findpat(const char *pattern, const char *pattern_end, bool unlisted, bool diffmode,
bool curtab_only)
FUNC_ATTR_NONNULL_ARG(1)
{
@@ -2229,7 +2245,7 @@ static int buf_time_compare(const void *s1, const void *s2)
/// For command line expansion of ":buf" and ":sbuf".
///
/// @return OK if matches found, FAIL otherwise.
-int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
+int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
{
int count = 0;
int round;
@@ -2251,20 +2267,20 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
STRCPY(patc, "\\(^\\|[\\/]\\)");
STRCPY(patc + 11, pat + 1);
} else {
- patc = (char *)pat;
+ patc = pat;
}
// attempt == 0: try match with '\<', match at start of word
// attempt == 1: try match without '\<', match anywhere
for (attempt = 0; attempt <= 1; attempt++) {
- if (attempt > 0 && (char_u *)patc == pat) {
+ if (attempt > 0 && patc == pat) {
break; // there was no anchor, no need to try again
}
regmatch_T regmatch;
regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC);
if (regmatch.regprog == NULL) {
- if ((char_u *)patc != pat) {
+ if (patc != pat) {
xfree(patc);
}
return FAIL;
@@ -2291,7 +2307,7 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
count++;
} else {
if (options & WILD_HOME_REPLACE) {
- p = (char *)home_replace_save(buf, (char_u *)p);
+ p = home_replace_save(buf, p);
} else {
p = xstrdup(p);
}
@@ -2300,7 +2316,7 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
matches[count].match = p;
count++;
} else {
- (*file)[count++] = (char_u *)p;
+ (*file)[count++] = p;
}
}
}
@@ -2322,7 +2338,7 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
}
}
- if ((char_u *)patc != pat) {
+ if (patc != pat) {
xfree(patc);
}
@@ -2334,12 +2350,12 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
// if the current buffer is first in the list, place it at the end
if (matches[0].buf == curbuf) {
for (int i = 1; i < count; i++) {
- (*file)[i - 1] = (char_u *)matches[i].match;
+ (*file)[i - 1] = matches[i].match;
}
- (*file)[count - 1] = (char_u *)matches[0].match;
+ (*file)[count - 1] = matches[0].match;
} else {
for (int i = 0; i < count; i++) {
- (*file)[i] = (char_u *)matches[i].match;
+ (*file)[i] = matches[i].match;
}
}
xfree(matches);
@@ -2355,9 +2371,9 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
static char *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case)
{
// First try the short file name, then the long file name.
- char *match = fname_match(rmp, (char *)buf->b_sfname, ignore_case);
+ char *match = fname_match(rmp, buf->b_sfname, ignore_case);
if (match == NULL && rmp->regprog != NULL) {
- match = fname_match(rmp, (char *)buf->b_ffname, ignore_case);
+ match = fname_match(rmp, buf->b_ffname, ignore_case);
}
return match;
}
@@ -2375,12 +2391,12 @@ static char *fname_match(regmatch_T *rmp, char *name, bool ignore_case)
if (name != NULL) {
// Ignore case when 'fileignorecase' or the argument is set.
rmp->rm_ic = p_fic || ignore_case;
- if (vim_regexec(rmp, (char_u *)name, (colnr_T)0)) {
+ if (vim_regexec(rmp, name, (colnr_T)0)) {
match = name;
} else if (rmp->regprog != NULL) {
// Replace $(HOME) with '~' and try matching again.
- p = (char *)home_replace_save(NULL, (char_u *)name);
- if (vim_regexec(rmp, (char_u *)p, (colnr_T)0)) {
+ p = home_replace_save(NULL, name);
+ if (vim_regexec(rmp, p, (colnr_T)0)) {
match = name;
}
xfree(p);
@@ -2407,16 +2423,14 @@ buf_T *buflist_findnr(int nr)
/// @param helptail for help buffers return tail only
///
/// @return a pointer to allocated memory, of NULL when failed.
-char_u *buflist_nr2name(int n, int fullname, int helptail)
+char *buflist_nr2name(int n, int fullname, int helptail)
{
- buf_T *buf;
-
- buf = buflist_findnr(n);
+ buf_T *buf = buflist_findnr(n);
if (buf == NULL) {
return NULL;
}
return home_replace_save(helptail ? buf : NULL,
- fullname ? buf->b_ffname : (char_u *)buf->b_fname);
+ fullname ? buf->b_ffname : buf->b_fname);
}
/// Set the line and column numbers for the given buffer and window
@@ -2462,8 +2476,11 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T
}
}
if (lnum != 0) {
- wip->wi_fpos.lnum = lnum;
- wip->wi_fpos.col = col;
+ wip->wi_mark.mark.lnum = lnum;
+ wip->wi_mark.mark.col = col;
+ if (win != NULL) {
+ wip->wi_mark.view = mark_view_make(win->w_topline, wip->wi_mark.mark);
+ }
}
if (copy_options && win != NULL) {
// Save the window-specific option values.
@@ -2581,24 +2598,23 @@ void get_winopts(buf_T *buf)
didset_window_options(curwin);
}
-/// Find the position (lnum and col) for the buffer 'buf' for the current
-/// window.
+/// Find the mark for the buffer 'buf' for the current window.
///
/// @return a pointer to no_position if no position is found.
-pos_T *buflist_findfpos(buf_T *buf)
+fmark_T *buflist_findfmark(buf_T *buf)
FUNC_ATTR_PURE
{
- static pos_T no_position = { 1, 0, 0 };
+ static fmark_T no_position = { { 1, 0, 0 }, 0, 0, { 0 }, NULL };
wininfo_T *const wip = find_wininfo(buf, false, false);
- return (wip == NULL) ? &no_position : &(wip->wi_fpos);
+ return (wip == NULL) ? &no_position : &(wip->wi_mark);
}
/// Find the lnum for the buffer 'buf' for the current window.
linenr_T buflist_findlnum(buf_T *buf)
FUNC_ATTR_PURE
{
- return buflist_findfpos(buf)->lnum;
+ return buflist_findfmark(buf)->mark.lnum;
}
/// List all known file names (for :files and :buffers command).
@@ -2710,16 +2726,14 @@ void buflist_list(exarg_T *eap)
/// Used by insert_reg() and cmdline_paste() for '#' register.
///
/// @return FAIL if not found, OK for success.
-int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum)
+int buflist_name_nr(int fnum, char **fname, linenr_T *lnum)
{
- buf_T *buf;
-
- buf = buflist_findnr(fnum);
+ buf_T *buf = buflist_findnr(fnum);
if (buf == NULL || buf->b_fname == NULL) {
return FAIL;
}
- *fname = (char_u *)buf->b_fname;
+ *fname = buf->b_fname;
*lnum = buflist_findlnum(buf);
return OK;
@@ -2774,16 +2788,16 @@ int setfname(buf_T *buf, char *ffname_arg, char *sfname_arg, bool message)
}
sfname = xstrdup(sfname);
#ifdef USE_FNAME_CASE
- path_fix_case((char_u *)sfname); // set correct case for short file name
+ path_fix_case(sfname); // set correct case for short file name
#endif
if (buf->b_sfname != buf->b_ffname) {
xfree(buf->b_sfname);
}
xfree(buf->b_ffname);
- buf->b_ffname = (char_u *)ffname;
- buf->b_sfname = (char_u *)sfname;
+ buf->b_ffname = ffname;
+ buf->b_sfname = sfname;
}
- buf->b_fname = (char *)buf->b_sfname;
+ buf->b_fname = buf->b_sfname;
if (!file_id_valid) {
buf->file_id_valid = false;
} else {
@@ -2797,22 +2811,20 @@ int setfname(buf_T *buf, char *ffname_arg, char *sfname_arg, bool message)
/// Crude way of changing the name of a buffer. Use with care!
/// The name should be relative to the current directory.
-void buf_set_name(int fnum, char_u *name)
+void buf_set_name(int fnum, char *name)
{
- buf_T *buf;
-
- buf = buflist_findnr(fnum);
+ buf_T *buf = buflist_findnr(fnum);
if (buf != NULL) {
if (buf->b_sfname != buf->b_ffname) {
xfree(buf->b_sfname);
}
xfree(buf->b_ffname);
- buf->b_ffname = vim_strsave(name);
+ buf->b_ffname = xstrdup(name);
buf->b_sfname = NULL;
// Allocate ffname and expand into full path. Also resolves .lnk
// files on Win32.
- fname_expand(buf, (char **)&buf->b_ffname, (char **)&buf->b_sfname);
- buf->b_fname = (char *)buf->b_sfname;
+ fname_expand(buf, &buf->b_ffname, &buf->b_sfname);
+ buf->b_fname = buf->b_sfname;
}
}
@@ -2838,12 +2850,10 @@ void buf_name_changed(buf_T *buf)
/// Used by do_one_cmd(), do_write() and do_ecmd().
///
/// @return the buffer.
-buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum)
+buf_T *setaltfname(char *ffname, char *sfname, linenr_T lnum)
{
- buf_T *buf;
-
// Create a buffer. 'buflisted' is not set if it's a new buffer
- buf = buflist_new(ffname, sfname, lnum, 0);
+ buf_T *buf = buflist_new(ffname, sfname, lnum, 0);
if (buf != NULL && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
curwin->w_alt_fnum = buf->b_fnum;
}
@@ -2854,29 +2864,27 @@ buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum)
/// Return NULL if there isn't any, and give error message if requested.
///
/// @param errmsg give error message
-char_u *getaltfname(bool errmsg)
+char *getaltfname(bool errmsg)
{
char *fname;
linenr_T dummy;
- if (buflist_name_nr(0, (char_u **)&fname, &dummy) == FAIL) {
+ if (buflist_name_nr(0, &fname, &dummy) == FAIL) {
if (errmsg) {
emsg(_(e_noalt));
}
return NULL;
}
- return (char_u *)fname;
+ return fname;
}
/// Add a file name to the buflist and return its number.
/// Uses same flags as buflist_new(), except BLN_DUMMY.
///
/// Used by qf_init(), main() and doarglist()
-int buflist_add(char_u *fname, int flags)
+int buflist_add(char *fname, int flags)
{
- buf_T *buf;
-
- buf = buflist_new(fname, NULL, (linenr_T)0, flags);
+ buf_T *buf = buflist_new(fname, NULL, (linenr_T)0, flags);
if (buf != NULL) {
return buf->b_fnum;
}
@@ -2910,10 +2918,10 @@ void buflist_altfpos(win_T *win)
/// Fname must have a full path (expanded by path_to_absolute()).
///
/// @param ffname full path name to check
-bool otherfile(char_u *ffname)
+bool otherfile(char *ffname)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- return otherfile_buf(curbuf, (char *)ffname, NULL, false);
+ return otherfile_buf(curbuf, ffname, NULL, false);
}
/// Check that "ffname" is not the same file as the file loaded in "buf".
@@ -3014,7 +3022,7 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate)
if (!fullname && curbuf->b_fname != NULL) {
name = curbuf->b_fname;
} else {
- name = (char *)curbuf->b_ffname;
+ name = curbuf->b_ffname;
}
home_replace(shorthelp ? curbuf : NULL, name, p,
(size_t)(IOSIZE - (p - buffer)), true);
@@ -3062,7 +3070,7 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate)
n);
validate_virtcol();
len = STRLEN(buffer);
- col_print((char_u *)buffer + len, IOSIZE - len,
+ col_print(buffer + len, IOSIZE - len,
(int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
}
@@ -3091,12 +3099,12 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate)
xfree(buffer);
}
-void col_print(char_u *buf, size_t buflen, int col, int vcol)
+void col_print(char *buf, size_t buflen, int col, int vcol)
{
if (col == vcol) {
- vim_snprintf((char *)buf, buflen, "%d", col);
+ vim_snprintf(buf, buflen, "%d", col);
} else {
- vim_snprintf((char *)buf, buflen, "%d-%d", col, vcol);
+ vim_snprintf(buf, buflen, "%d-%d", col, vcol);
}
}
@@ -3135,18 +3143,16 @@ void maketitle(void)
if (*p_titlestring != NUL) {
if (stl_syntax & STL_IN_TITLE) {
int use_sandbox = false;
- int save_called_emsg = called_emsg;
+ const int called_emsg_before = called_emsg;
use_sandbox = was_set_insecurely(curwin, "titlestring", 0);
- called_emsg = false;
build_stl_str_hl(curwin, buf, sizeof(buf),
(char *)p_titlestring, use_sandbox,
0, maxlen, NULL, NULL);
title_str = buf;
- if (called_emsg) {
+ if (called_emsg > called_emsg_before) {
set_string_option_direct("titlestring", -1, "", OPT_FREE, SID_ERROR);
}
- called_emsg |= save_called_emsg;
} else {
title_str = (char *)p_titlestring;
}
@@ -3191,7 +3197,7 @@ void maketitle(void)
// Get path of file, replace home dir with ~.
*buf_p++ = ' ';
*buf_p++ = '(';
- home_replace(curbuf, (char *)curbuf->b_ffname, buf_p,
+ home_replace(curbuf, curbuf->b_ffname, buf_p,
(SPACE_FOR_DIR - (size_t)(buf_p - buf)), true);
#ifdef BACKSLASH_IN_FILENAME
// Avoid "c:/name" to be reduced to "c".
@@ -3200,7 +3206,7 @@ void maketitle(void)
}
#endif
// Remove the file name.
- char *p = (char *)path_tail_with_sep((char_u *)buf_p);
+ char *p = path_tail_with_sep(buf_p);
if (p == buf_p) {
// Must be a help buffer.
xstrlcpy(buf_p, _("help"), SPACE_FOR_DIR - (size_t)(buf_p - buf));
@@ -3235,7 +3241,7 @@ void maketitle(void)
if (maxlen > 0) {
// Make it shorter by removing a bit in the middle.
if (vim_strsize(buf) > maxlen) {
- trunc_string((char_u *)buf, (char_u *)buf, maxlen, sizeof(buf));
+ trunc_string(buf, buf, maxlen, sizeof(buf));
}
}
title_str = buf;
@@ -3251,17 +3257,15 @@ void maketitle(void)
if (*p_iconstring != NUL) {
if (stl_syntax & STL_IN_ICON) {
int use_sandbox = false;
- int save_called_emsg = called_emsg;
+ const int called_emsg_before = called_emsg;
use_sandbox = was_set_insecurely(curwin, "iconstring", 0);
- called_emsg = false;
build_stl_str_hl(curwin, icon_str, sizeof(buf),
(char *)p_iconstring, use_sandbox,
0, 0, NULL, NULL);
- if (called_emsg) {
+ if (called_emsg > called_emsg_before) {
set_string_option_direct("iconstring", -1, "", OPT_FREE, SID_ERROR);
}
- called_emsg |= save_called_emsg;
} else {
icon_str = (char *)p_iconstring;
}
@@ -3270,18 +3274,18 @@ void maketitle(void)
if (buf_spname(curbuf) != NULL) {
buf_p = buf_spname(curbuf);
} else { // use file name only in icon
- buf_p = path_tail((char *)curbuf->b_ffname);
+ buf_p = path_tail(curbuf->b_ffname);
}
*icon_str = NUL;
// Truncate name at 100 bytes.
len = (int)STRLEN(buf_p);
if (len > 100) {
len -= 100;
- len += mb_tail_off((char_u *)buf_p, (char_u *)buf_p + len) + 1;
+ len += mb_tail_off(buf_p, buf_p + len) + 1;
buf_p += len;
}
STRCPY(icon_str, buf_p);
- trans_characters((char_u *)icon_str, IOSIZE);
+ trans_characters(icon_str, IOSIZE);
}
}
@@ -3679,7 +3683,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
// The first digit group is the item's min width
if (ascii_isdigit(*fmt_p)) {
- minwid = getdigits_int((char_u **)&fmt_p, false, 0);
+ minwid = getdigits_int(&fmt_p, false, 0);
}
// User highlight groups override the min width field
@@ -3762,7 +3766,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
if (*fmt_p == '.') {
fmt_p++;
if (ascii_isdigit(*fmt_p)) {
- maxwid = getdigits_int((char_u **)&fmt_p, false, 50);
+ maxwid = getdigits_int(&fmt_p, false, 50);
}
}
@@ -3815,11 +3819,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_san
if (buf_spname(wp->w_buffer) != NULL) {
STRLCPY(NameBuff, buf_spname(wp->w_buffer), MAXPATHL);
} else {
- char *t = (opt == STL_FULLPATH) ? (char *)wp->w_buffer->b_ffname
+ char *t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
: wp->w_buffer->b_fname;
home_replace(wp->w_buffer, t, (char *)NameBuff, MAXPATHL, true);
}
- trans_characters(NameBuff, MAXPATHL);
+ trans_characters((char *)NameBuff, MAXPATHL);
if (opt != STL_FILENAME) {
str = (char *)NameBuff;
} else {
@@ -4699,7 +4703,7 @@ void do_arg_all(int count, int forceit, int keep_tabs)
if (i < alist->al_ga.ga_len
&& (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
|| path_full_compare(alist_name(&AARGLIST(alist)[i]),
- (char *)buf->b_ffname,
+ buf->b_ffname,
true, true) & kEqualFiles)) {
int weight = 1;
@@ -5267,9 +5271,9 @@ bool bt_terminal(const buf_T *const buf)
return buf != NULL && buf->b_p_bt[0] == 't';
}
-/// @return true if "buf" is a "nofile", "acwrite", "terminal" or "prompt" /
+/// @return true if "buf" is a "nofile", "acwrite", "terminal" or "prompt"
/// buffer. This means the buffer name is not a file name.
-bool bt_nofile(const buf_T *const buf)
+bool bt_nofilename(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
@@ -5278,6 +5282,13 @@ bool bt_nofile(const buf_T *const buf)
|| buf->b_p_bt[0] == 'p');
}
+/// @return true if "buf" has 'buftype' set to "nofile".
+bool bt_nofile(const buf_T *const buf)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return buf != NULL && buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f';
+}
+
/// @return true if "buf" is a "nowrite", "nofile", "terminal" or "prompt"
/// buffer.
bool bt_dontwrite(const buf_T *const buf)
@@ -5329,7 +5340,7 @@ char *buf_spname(buf_T *buf)
}
// There is no _file_ when 'buftype' is "nofile", b_sfname
// contains the name as specified by the user.
- if (bt_nofile(buf)) {
+ if (bt_nofilename(buf)) {
if (buf->b_fname != NULL) {
return buf->b_fname;
}
@@ -5567,7 +5578,7 @@ bool buf_contents_changed(buf_T *buf)
aucmd_prepbuf(&aco, newbuf);
if (ml_open(curbuf) == OK
- && readfile((char *)buf->b_ffname, buf->b_fname,
+ && readfile(buf->b_ffname, buf->b_fname,
(linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
&ea, READ_NEW | READ_DUMMY, false) == OK) {
// compare the two files line by line
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 5d1135f91c..8c70765d30 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -283,8 +283,8 @@ typedef struct {
struct wininfo_S {
wininfo_T *wi_next; // next entry or NULL for last entry
wininfo_T *wi_prev; // previous entry or NULL for first entry
- win_T *wi_win; // pointer to window that did set wi_fpos
- pos_T wi_fpos; // last cursor position in the file
+ win_T *wi_win; // pointer to window that did set wi_mark
+ fmark_T wi_mark; // last cursor mark in the file
bool wi_optset; // true when wi_opt has useful values
winopt_T wi_opt; // local window options
bool wi_fold_manual; // copy of w_fold_manual
@@ -545,8 +545,8 @@ struct file_buffer {
// b_sfname is the name as the user typed it (or NULL).
// b_fname is the same as b_sfname, unless ":cd" has been done,
// then it is the same as b_ffname (NULL for no name).
- char_u *b_ffname; // full path file name, allocated
- char_u *b_sfname; // short file name, allocated, may be equal to
+ char *b_ffname; // full path file name, allocated
+ char *b_sfname; // short file name, allocated, may be equal to
// b_ffname
char *b_fname; // current file name, points to b_ffname or
// b_sfname
@@ -829,7 +829,7 @@ struct file_buffer {
int b_start_eol; // last line had eol when it was read
int b_start_ffc; // first char of 'ff' when edit started
- char_u *b_start_fenc; // 'fileencoding' when edit started or NULL
+ char *b_start_fenc; // 'fileencoding' when edit started or NULL
int b_bad_char; // "++bad=" argument when edit started or 0
int b_start_bomb; // 'bomb' when it was read
@@ -855,7 +855,7 @@ struct file_buffer {
// are not used! Use the B_SPELL macro to
// access b_spell without #ifdef.
- char_u *b_prompt_text; // set by prompt_setprompt()
+ char *b_prompt_text; // set by prompt_setprompt()
Callback b_prompt_callback; // set by prompt_setcallback()
Callback b_prompt_interrupt; // set by prompt_setinterrupt()
int b_prompt_insert; // value for restart_edit when entering
@@ -963,8 +963,8 @@ struct tabpage_S {
frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots
ScopeDictDictItem tp_winvar; ///< Variable for "t:" Dictionary.
dict_T *tp_vars; ///< Internal variables, local to tab page.
- char_u *tp_localdir; ///< Absolute path of local cwd or NULL.
- char_u *tp_prevdir; ///< Previous directory.
+ char *tp_localdir; ///< Absolute path of local cwd or NULL.
+ char *tp_prevdir; ///< Previous directory.
};
/*
@@ -1061,7 +1061,7 @@ struct matchitem {
matchitem_T *next;
int id; ///< match ID
int priority; ///< match priority
- char_u *pattern; ///< pattern to highlight
+ char *pattern; ///< pattern to highlight
regmmatch_T match; ///< regexp program for pattern
posmatch_T pos; ///< position matches
match_T hl; ///< struct for doing the actual highlighting
@@ -1144,8 +1144,9 @@ enum {
MENU_INDEX_OP_PENDING = 3,
MENU_INDEX_INSERT = 4,
MENU_INDEX_CMDLINE = 5,
- MENU_INDEX_TIP = 6,
- MENU_MODES = 7,
+ MENU_INDEX_TERMINAL = 6,
+ MENU_INDEX_TIP = 7,
+ MENU_MODES = 8,
};
typedef struct VimMenu vimmenu_T;
@@ -1408,8 +1409,8 @@ struct window_S {
// out of range!)
int w_arg_idx_invalid; // editing another file than w_arg_idx
- char_u *w_localdir; // absolute path of local directory or NULL
- char_u *w_prevdir; // previous directory
+ char *w_localdir; // absolute path of local directory or NULL
+ char *w_prevdir; // previous directory
// Options local to a window.
// They are local because they influence the layout of the window or
// depend on the window layout.
@@ -1423,10 +1424,10 @@ struct window_S {
uint32_t w_p_wbr_flags; // flags for 'winbar'
uint32_t w_p_fde_flags; // flags for 'foldexpr'
uint32_t w_p_fdt_flags; // flags for 'foldtext'
- int *w_p_cc_cols; // array of columns to highlight or NULL
- char_u w_p_culopt_flags; // flags for cursorline highlighting
- long w_p_siso; // 'sidescrolloff' local value
- long w_p_so; // 'scrolloff' local value
+ int *w_p_cc_cols; // array of columns to highlight or NULL
+ uint8_t w_p_culopt_flags; // flags for cursorline highlighting
+ long w_p_siso; // 'sidescrolloff' local value
+ long w_p_so; // 'scrolloff' local value
int w_briopt_min; // minimum width for breakindent
int w_briopt_shift; // additional shift for breakindent
diff --git a/src/nvim/change.c b/src/nvim/change.c
index a383fe6bb3..4568b71fd9 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -149,7 +149,13 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
// set the '. mark
if ((cmdmod.cmod_flags & CMOD_KEEPJUMPS) == 0) {
- RESET_FMARK(&curbuf->b_last_change, ((pos_T) { lnum, col, 0 }), 0);
+ fmarkv_T view = INIT_FMARKV;
+ // Set the markview only if lnum is visible, as changes might be done
+ // outside of the current window view.
+ if (lnum >= curwin->w_topline && lnum <= curwin->w_botline) {
+ view = mark_view_make(curwin->w_topline, curwin->w_cursor);
+ }
+ RESET_FMARK(&curbuf->b_last_change, ((pos_T) { lnum, col, 0 }), curbuf->handle, view);
// Create a new entry if a new undo-able change was started or we
// don't have an entry yet.
@@ -1053,7 +1059,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
old_cursor = curwin->w_cursor;
ptr = saved_line;
if (flags & OPENLINE_DO_COM) {
- lead_len = get_leader_len(ptr, NULL, false, true);
+ lead_len = get_leader_len((char *)ptr, NULL, false, true);
} else {
lead_len = 0;
}
@@ -1066,7 +1072,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
newindent = get_indent();
}
if (flags & OPENLINE_DO_COM) {
- lead_len = get_leader_len(ptr, NULL, false, true);
+ lead_len = get_leader_len((char *)ptr, NULL, false, true);
} else {
lead_len = 0;
}
@@ -1185,14 +1191,14 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// This may then be inserted in front of the new line.
end_comment_pending = NUL;
if (flags & OPENLINE_DO_COM) {
- lead_len = get_leader_len(saved_line, &lead_flags, dir == BACKWARD, true);
+ lead_len = get_leader_len((char *)saved_line, (char **)&lead_flags, dir == BACKWARD, true);
if (lead_len == 0 && curbuf->b_p_cin && do_cindent && dir == FORWARD
&& (!has_format_option(FO_NO_OPEN_COMS) || (flags & OPENLINE_FORMAT))) {
// Check for a line comment after code.
comment_start = check_linecomment(saved_line);
if (comment_start != MAXCOL) {
- lead_len = get_leader_len(saved_line + comment_start,
- &lead_flags, false, true);
+ lead_len = get_leader_len((char *)saved_line + comment_start,
+ (char **)&lead_flags, false, true);
if (lead_len != 0) {
lead_len += comment_start;
if (did_do_comment != NULL) {
@@ -1232,7 +1238,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
}
// find start of middle part
- (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ",");
+ (void)copy_option_part((char **)&p, (char *)lead_middle, COM_MAX_LEN, ",");
require_blank = false;
}
@@ -1243,7 +1249,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
}
p++;
}
- (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ",");
+ (void)copy_option_part((char **)&p, (char *)lead_middle, COM_MAX_LEN, ",");
while (*p && p[-1] != ':') { // find end of end flags
// Check whether we allow automatic ending of comments
@@ -1252,7 +1258,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
}
p++;
}
- size_t n = copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
+ size_t n = copy_option_part((char **)&p, (char *)lead_end, COM_MAX_LEN, ",");
if (end_comment_pending == -1) { // we can set it now
end_comment_pending = lead_end[n - 1];
@@ -1371,7 +1377,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
if (*p == COM_RIGHT || *p == COM_LEFT) {
c = *p++;
} else if (ascii_isdigit(*p) || *p == '-') {
- off = getdigits_int(&p, true, 0);
+ off = getdigits_int((char **)&p, true, 0);
} else {
p++;
}
@@ -1875,16 +1881,16 @@ void del_lines(long nlines, bool undo)
/// When "flags" is not NULL, it is set to point to the flags of the recognized comment leader.
/// "backward" must be true for the "O" command.
/// If "include_space" is set, include trailing whitespace while calculating the length.
-int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_space)
+int get_leader_len(char *line, char **flags, bool backward, bool include_space)
{
int j;
int got_com = false;
char part_buf[COM_MAX_LEN]; // buffer for one option part
- char_u *string; // pointer to comment string
- char_u *list;
+ char *string; // pointer to comment string
+ char *list;
int middle_match_len = 0;
- char_u *prev_list;
- char_u *saved_flags = NULL;
+ char *prev_list;
+ char *saved_flags = NULL;
int result = 0;
int i = 0;
@@ -1896,15 +1902,15 @@ int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_spa
while (line[i] != NUL) {
// scan through the 'comments' option for a match
int found_one = false;
- for (list = curbuf->b_p_com; *list;) {
+ for (list = (char *)curbuf->b_p_com; *list;) {
// Get one option part into part_buf[]. Advance "list" to next
// one. Put "string" at start of string.
if (!got_com && flags != NULL) {
*flags = list; // remember where flags started
}
prev_list = list;
- (void)copy_option_part(&list, (char_u *)part_buf, COM_MAX_LEN, ",");
- string = (char_u *)vim_strchr(part_buf, ':');
+ (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
+ string = vim_strchr(part_buf, ':');
if (string == NULL) { // missing ':', ignore this part
continue;
}
@@ -2017,15 +2023,15 @@ int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_spa
///
/// When "flags" is not null, it is set to point to the flags describing the
/// recognized comment leader.
-int get_last_leader_offset(char_u *line, char_u **flags)
+int get_last_leader_offset(char *line, char **flags)
{
int result = -1;
int j;
int lower_check_bound = 0;
- char_u *string;
- char_u *com_leader;
- char_u *com_flags;
- char_u *list;
+ char *string;
+ char *com_leader;
+ char *com_flags;
+ char *list;
char part_buf[COM_MAX_LEN]; // buffer for one option part
// Repeat to match several nested comment strings.
@@ -2033,13 +2039,13 @@ int get_last_leader_offset(char_u *line, char_u **flags)
while (--i >= lower_check_bound) {
// scan through the 'comments' option for a match
int found_one = false;
- for (list = curbuf->b_p_com; *list;) {
- char_u *flags_save = list;
+ for (list = (char *)curbuf->b_p_com; *list;) {
+ char *flags_save = list;
// Get one option part into part_buf[]. Advance list to next one.
// put string at start of string.
- (void)copy_option_part(&list, (char_u *)part_buf, COM_MAX_LEN, ",");
- string = (char_u *)vim_strchr(part_buf, ':');
+ (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
+ string = vim_strchr(part_buf, ':');
if (string == NULL) { // If everything is fine, this cannot actually
// happen.
continue;
@@ -2118,14 +2124,14 @@ int get_last_leader_offset(char_u *line, char_u **flags)
}
len1 = (int)STRLEN(com_leader);
- for (list = curbuf->b_p_com; *list;) {
- char_u *flags_save = list;
+ for (list = (char *)curbuf->b_p_com; *list;) {
+ char *flags_save = list;
- (void)copy_option_part(&list, (char_u *)part_buf2, COM_MAX_LEN, ",");
+ (void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ",");
if (flags_save == com_flags) {
continue;
}
- string = (char_u *)vim_strchr(part_buf2, ':');
+ string = vim_strchr(part_buf2, ':');
string++;
while (ascii_iswhite(*string)) {
string++;
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 885ebca214..028dd70eb2 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -162,7 +162,7 @@ int buf_init_chartab(buf_T *buf, int global)
}
if (ascii_isdigit(*p)) {
- c = getdigits_int((char_u **)&p, true, 0);
+ c = getdigits_int((char **)&p, true, 0);
} else {
c = mb_ptr2char_adv(&p);
}
@@ -172,7 +172,7 @@ int buf_init_chartab(buf_T *buf, int global)
p++;
if (ascii_isdigit(*p)) {
- c2 = getdigits_int((char_u **)&p, true, 0);
+ c2 = getdigits_int((char **)&p, true, 0);
} else {
c2 = mb_ptr2char_adv(&p);
}
@@ -265,7 +265,7 @@ int buf_init_chartab(buf_T *buf, int global)
///
/// @param buf
/// @param bufsize
-void trans_characters(char_u *buf, int bufsize)
+void trans_characters(char *buf, int bufsize)
{
char_u *trs; // translated character
int len = (int)STRLEN(buf); // length of string needing translation
@@ -274,10 +274,10 @@ void trans_characters(char_u *buf, int bufsize)
while (*buf != 0) {
int trs_len; // length of trs[]
// Assume a multi-byte character doesn't need translation.
- if ((trs_len = utfc_ptr2len((char *)buf)) > 1) {
+ if ((trs_len = utfc_ptr2len(buf)) > 1) {
len -= trs_len;
} else {
- trs = transchar_byte(*buf);
+ trs = transchar_byte((uint8_t)(*buf));
trs_len = (int)STRLEN(trs);
if (trs_len > 1) {
@@ -1302,7 +1302,7 @@ char_u *skiptowhite(const char_u *p)
/// @param p
///
/// @return Pointer to the next whitespace character.
-char_u *skiptowhite_esc(char_u *p)
+char *skiptowhite_esc(char *p)
FUNC_ATTR_PURE
{
while (*p != ' ' && *p != '\t' && *p != NUL) {
@@ -1364,9 +1364,9 @@ intmax_t getdigits(char_u **pp, bool strict, intmax_t def)
/// Gets an int number from a string.
///
/// @see getdigits
-int getdigits_int(char_u **pp, bool strict, int def)
+int getdigits_int(char **pp, bool strict, int def)
{
- intmax_t number = getdigits(pp, strict, def);
+ intmax_t number = getdigits((char_u **)pp, strict, def);
#if SIZEOF_INTMAX_T > SIZEOF_INT
if (strict) {
assert(number >= INT_MIN && number <= INT_MAX);
diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c
index c7c7e6ae58..62cf60e03b 100644
--- a/src/nvim/cursor_shape.c
+++ b/src/nvim/cursor_shape.c
@@ -189,7 +189,7 @@ char *parse_shape_opt(int what)
if (!ascii_isdigit(*p)) {
return N_("E548: digit expected");
}
- int n = getdigits_int((char_u **)&p, false, 0);
+ int n = getdigits_int(&p, false, 0);
if (len == 3) { // "ver" or "hor"
if (n == 0) {
return N_("E549: Illegal percentage");
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index 803427dd17..0eaff06833 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -523,7 +523,7 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
if (bp->dbg_type == DBG_FUNC) {
bp->dbg_name = vim_strsave((char_u *)p);
} else if (here) {
- bp->dbg_name = vim_strsave(curbuf->b_ffname);
+ bp->dbg_name = vim_strsave((char_u *)curbuf->b_ffname);
} else if (bp->dbg_type == DBG_EXPR) {
bp->dbg_name = vim_strsave((char_u *)p);
bp->dbg_val = eval_expr_no_emsg(bp);
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index e4d77cec9c..75021e90d6 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -2143,7 +2143,7 @@ int diffopt_changed(void)
diff_flags_new |= DIFF_FILLER;
} else if ((STRNCMP(p, "context:", 8) == 0) && ascii_isdigit(p[8])) {
p += 8;
- diff_context_new = getdigits_int(&p, false, diff_context_new);
+ diff_context_new = getdigits_int((char **)&p, false, diff_context_new);
} else if (STRNCMP(p, "iblank", 6) == 0) {
p += 6;
diff_flags_new |= DIFF_IBLANK;
@@ -2167,7 +2167,7 @@ int diffopt_changed(void)
diff_flags_new |= DIFF_VERTICAL;
} else if ((STRNCMP(p, "foldcolumn:", 11) == 0) && ascii_isdigit(p[11])) {
p += 11;
- diff_foldcolumn_new = getdigits_int(&p, false, diff_foldcolumn_new);
+ diff_foldcolumn_new = getdigits_int((char **)&p, false, diff_foldcolumn_new);
} else if (STRNCMP(p, "hiddenoff", 9) == 0) {
p += 9;
diff_flags_new |= DIFF_HIDDEN_OFF;
@@ -2562,7 +2562,7 @@ void ex_diffgetput(exarg_T *eap)
// digits only
i = (int)atol(eap->arg);
} else {
- i = buflist_findpat((char_u *)eap->arg, p, false, true, false);
+ i = buflist_findpat(eap->arg, (char *)p, false, true, false);
if (i < 0) {
// error message already given
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index cbff15c84a..355900c93f 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -1683,7 +1683,7 @@ void putdigraph(char_u *str)
emsg(_(e_number_exp));
return;
}
- int n = getdigits_int(&str, true, 0);
+ int n = getdigits_int((char **)&str, true, 0);
registerdigraph(char1, char2, n);
}
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 1223d98fbf..0571e71cb5 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -136,7 +136,6 @@ static char *ctrl_x_mode_names[] = {
};
static char e_hitend[] = N_("Hit end of paragraph");
-static char e_complwin[] = N_("E839: Completion function changed window");
static char e_compldel[] = N_("E840: Completion function deleted text");
/*
@@ -1409,14 +1408,9 @@ bool edit(int cmdchar, bool startln, long count)
// Don't allow changes in the buffer while editing the cmdline. The
// caller of getcmdline() may get confused.
- if (textlock != 0) {
- emsg(_(e_secure));
- return false;
- }
-
// Don't allow recursive insert mode when busy with completion.
- if (compl_started || compl_busy || pum_visible()) {
- emsg(_(e_secure));
+ if (textlock != 0 || compl_started || compl_busy || pum_visible()) {
+ emsg(_(e_textlock));
return false;
}
@@ -1618,7 +1612,7 @@ char_u *buf_prompt_text(const buf_T *const buf)
if (buf->b_prompt_text == NULL) {
return (char_u *)"% ";
}
- return buf->b_prompt_text;
+ return (char_u *)buf->b_prompt_text;
}
// Return the effective prompt for the current buffer.
@@ -2955,7 +2949,7 @@ static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, i
/* Expand wildcards in the dictionary name, but do not allow
* backticks (for security, the 'dict' option may have been set in
* a modeline). */
- copy_option_part(&dict, buf, LSIZE, ",");
+ copy_option_part((char **)&dict, (char *)buf, LSIZE, ",");
if (!thesaurus && STRCMP(buf, "spell") == 0) {
count = -1;
} else if (vim_strchr((char *)buf, '`') != NULL
@@ -3020,7 +3014,7 @@ static void ins_compl_files(int count, char_u **files, int thesaurus, int flags,
while (!got_int && !compl_interrupted
&& !vim_fgets(buf, LSIZE, fp)) {
ptr = buf;
- while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) {
+ while (vim_regexec(regmatch, (char *)buf, (colnr_T)(ptr - buf))) {
ptr = regmatch->startp[0];
if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) {
ptr = find_line_end(ptr);
@@ -3944,8 +3938,6 @@ static void expand_by_function(int type, char_u *base)
dict_T *matchdict = NULL;
char_u *funcname;
pos_T pos;
- win_T *curwin_save;
- buf_T *curbuf_save;
typval_T rettv;
const int save_State = State;
@@ -3964,8 +3956,10 @@ static void expand_by_function(int type, char_u *base)
args[1].vval.v_string = base != NULL ? (char *)base : "";
pos = curwin->w_cursor;
- curwin_save = curwin;
- curbuf_save = curbuf;
+ // Lock the text to avoid weird things from happening. Also disallow
+ // switching to another window, it should not be needed and may end up in
+ // Insert mode in another buffer.
+ textlock++;
// Call a function, which returns a list or dict.
if (call_vim_function((char *)funcname, 2, args, &rettv) == OK) {
@@ -3984,11 +3978,8 @@ static void expand_by_function(int type, char_u *base)
break;
}
}
+ textlock--;
- if (curwin_save != curwin || curbuf_save != curbuf) {
- emsg(_(e_complwin));
- goto theend;
- }
curwin->w_cursor = pos; // restore the cursor position
validate_cursor();
if (!equalpos(curwin->w_cursor, pos)) {
@@ -4223,9 +4214,9 @@ static int ins_compl_get_exp(pos_T *ini)
msg_hist_off = true; // reset in msg_trunc_attr()
vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
ins_buf->b_fname == NULL
- ? (char_u *)buf_spname(ins_buf)
+ ? buf_spname(ins_buf)
: ins_buf->b_sfname == NULL
- ? (char_u *)ins_buf->b_fname
+ ? ins_buf->b_fname
: ins_buf->b_sfname);
(void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R));
} else if (*e_cpt == NUL) {
@@ -4257,7 +4248,7 @@ static int ins_compl_get_exp(pos_T *ini)
}
// in any case e_cpt is advanced to the next entry
- (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ",");
+ (void)copy_option_part((char **)&e_cpt, (char *)IObuff, IOSIZE, ",");
found_all = true;
if (type == -1) {
@@ -4313,7 +4304,7 @@ static int ins_compl_get_exp(pos_T *ini)
if (find_tags(compl_pattern, &num_matches, &matches,
TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
| (l_ctrl_x_mode != CTRL_X_NORMAL ? TAG_VERBOSE : 0),
- TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) {
+ TAG_MANY, (char_u *)curbuf->b_ffname) == OK && num_matches > 0) {
ins_compl_add_matches(num_matches, matches, p_ic);
}
g_tag_at_cursor = false;
@@ -4516,7 +4507,8 @@ static int ins_compl_get_exp(pos_T *ini)
}
}
}
- if (ins_compl_add_infercase(ptr, len, p_ic, ins_buf == curbuf ? NULL : ins_buf->b_sfname,
+ if (ins_compl_add_infercase(ptr, len, p_ic,
+ ins_buf == curbuf ? NULL : (char_u *)ins_buf->b_sfname,
0, cont_s_ipos) != NOTDONE) {
found_new_match = OK;
break;
@@ -5225,8 +5217,6 @@ static int ins_complete(int c, bool enable_pum)
// set to 1 to obtain the length of text to use for completion.
char_u *funcname;
pos_T pos;
- win_T *curwin_save;
- buf_T *curbuf_save;
const int save_State = State;
// Call 'completefunc' or 'omnifunc' and get pattern length as a string
@@ -5247,15 +5237,11 @@ static int ins_complete(int c, bool enable_pum)
args[1].vval.v_string = "";
pos = curwin->w_cursor;
- curwin_save = curwin;
- curbuf_save = curbuf;
+ textlock++;
colnr_T col = (colnr_T)call_func_retnr((char *)funcname, 2, args);
+ textlock--;
State = save_State;
- if (curwin_save != curwin || curbuf_save != curbuf) {
- emsg(_(e_complwin));
- return FAIL;
- }
curwin->w_cursor = pos; // restore the cursor position
validate_cursor();
if (!equalpos(curwin->w_cursor, pos)) {
@@ -5773,21 +5759,18 @@ void insertchar(int c, int flags, int second_indent)
// Check whether this character should end a comment.
if (did_ai && c == end_comment_pending) {
- char_u *line;
char_u lead_end[COM_MAX_LEN]; // end-comment string
- int i;
- /*
- * Need to remove existing (middle) comment leader and insert end
- * comment leader. First, check what comment leader we can find.
- */
- i = get_leader_len(line = get_cursor_line_ptr(), &p, false, true);
+ // Need to remove existing (middle) comment leader and insert end
+ // comment leader. First, check what comment leader we can find.
+ char_u *line = get_cursor_line_ptr();
+ int i = get_leader_len((char *)line, (char **)&p, false, true);
if (i > 0 && vim_strchr((char *)p, COM_MIDDLE) != NULL) { // Just checking
// Skip middle-comment string
while (*p && p[-1] != ':') { // find end of middle flags
p++;
}
- int middle_len = (int)copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
+ int middle_len = (int)copy_option_part((char **)&p, (char *)lead_end, COM_MAX_LEN, ",");
// Don't count trailing white space for middle_len
while (middle_len > 0 && ascii_iswhite(lead_end[middle_len - 1])) {
middle_len--;
@@ -5797,7 +5780,7 @@ void insertchar(int c, int flags, int second_indent)
while (*p && p[-1] != ':') { // find end of end flags
p++;
}
- int end_len = (int)copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
+ int end_len = (int)copy_option_part((char **)&p, (char *)lead_end, COM_MAX_LEN, ",");
// Skip white space before the cursor
i = curwin->w_cursor.col;
@@ -5974,12 +5957,12 @@ static void internal_format(int textwidth, int second_indent, int flags, int for
// Don't break until after the comment leader
if (do_comments) {
char_u *line = get_cursor_line_ptr();
- leader_len = get_leader_len(line, NULL, false, true);
+ leader_len = get_leader_len((char *)line, NULL, false, true);
if (leader_len == 0 && curbuf->b_p_cin) {
// Check for a line comment after code.
int comment_start = check_linecomment(line);
if (comment_start != MAXCOL) {
- leader_len = get_leader_len(line + comment_start, NULL, false, true);
+ leader_len = get_leader_len((char *)line + comment_start, NULL, false, true);
if (leader_len != 0) {
leader_len += comment_start;
}
@@ -6374,7 +6357,7 @@ void auto_format(bool trailblank, bool prev_line)
// With the 'c' flag in 'formatoptions' and 't' missing: only format
// comments.
if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP)
- && get_leader_len(old, NULL, false, true) == 0) {
+ && get_leader_len((char *)old, NULL, false, true) == 0) {
return;
}
@@ -7332,7 +7315,7 @@ static void replace_do_bs(int limit_col)
}
/// Check that C-indenting is on.
-static bool cindent_on(void)
+bool cindent_on(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL);
@@ -7928,7 +7911,8 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
// Remember the last Insert position in the '^ mark.
if ((cmdmod.cmod_flags & CMOD_KEEPJUMPS) == 0) {
- RESET_FMARK(&curbuf->b_last_insert, curwin->w_cursor, curbuf->b_fnum);
+ fmarkv_T view = mark_view_make(curwin->w_topline, curwin->w_cursor);
+ RESET_FMARK(&curbuf->b_last_insert, curwin->w_cursor, curbuf->b_fnum, view);
}
/*
@@ -7936,13 +7920,8 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
* Don't do it for CTRL-O, unless past the end of the line.
*/
if (!nomove
- && (curwin->w_cursor.col != 0
- || curwin->w_cursor.coladd > 0
- )
- && (restart_edit == NUL
- || (gchar_cursor() == NUL
- && !VIsual_active
- ))
+ && (curwin->w_cursor.col != 0 || curwin->w_cursor.coladd > 0)
+ && (restart_edit == NUL || (gchar_cursor() == NUL && !VIsual_active))
&& !revins_on) {
if (curwin->w_cursor.coladd > 0 || get_ve_flags() == VE_ALL) {
oneleft();
@@ -8374,7 +8353,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
}
// delete characters until we are at or before want_vcol
- while (vcol > want_vcol
+ while (vcol > want_vcol && curwin->w_cursor.col > 0
&& (cc = *(get_cursor_pos_ptr() - 1), ascii_iswhite(cc))) {
ins_bs_one(&vcol);
}
@@ -8558,14 +8537,12 @@ static void ins_mousescroll(int dir)
}
// Don't scroll the window in which completion is being done.
- if (!pum_visible()
- || curwin != old_curwin) {
+ if (!pum_visible() || curwin != old_curwin) {
if (dir == MSCR_DOWN || dir == MSCR_UP) {
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
- scroll_redraw(dir,
- (curwin->w_botline - curwin->w_topline));
+ scroll_redraw(dir, (long)(curwin->w_botline - curwin->w_topline));
} else {
- scroll_redraw(dir, 3L);
+ scroll_redraw(dir, p_mousescroll_vert);
}
} else {
mouse_scroll_horiz(dir);
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index bb2404750b..096fcba981 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -986,7 +986,7 @@ void restore_vimvar(int idx, typval_T *save_tv)
vimvars[idx].vv_tv = *save_tv;
if (vimvars[idx].vv_type == VAR_UNKNOWN) {
- hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key);
+ hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key);
if (HASHITEM_EMPTY(hi)) {
internal_error("restore_vimvar()");
} else {
@@ -2822,7 +2822,7 @@ void ex_lockvar(exarg_T *eap)
if (eap->forceit) {
deep = -1;
} else if (ascii_isdigit(*arg)) {
- deep = getdigits_int((char_u **)&arg, false, -1);
+ deep = getdigits_int(&arg, false, -1);
arg = skipwhite(arg);
}
@@ -3019,7 +3019,7 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit)
}
}
- hashitem_T *hi = hash_find(ht, (const char_u *)varname);
+ hashitem_T *hi = hash_find(ht, varname);
if (HASHITEM_EMPTY(hi)) {
hi = find_hi_in_scoped_ht(name, &ht);
}
@@ -6554,7 +6554,8 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
const char *prompt = "";
const char *defstr = "";
- const char *cancelreturn = NULL;
+ typval_T *cancelreturn = NULL;
+ typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE;
const char *xp_name = NULL;
Callback input_callback = { .type = kCallbackNone };
char prompt_buf[NUMBUFLEN];
@@ -6576,13 +6577,9 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
if (defstr == NULL) {
return;
}
- cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"),
- cancelreturn_buf, def);
- if (cancelreturn == NULL) { // error
- return;
- }
- if (*cancelreturn == NUL) {
- cancelreturn = NULL;
+ dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn"));
+ if (cancelreturn_di != NULL) {
+ cancelreturn = &cancelreturn_di->di_tv;
}
xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"),
xp_name_buf, def);
@@ -6606,15 +6603,16 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
return;
}
if (argvars[2].v_type != VAR_UNKNOWN) {
- const char *const arg2 = tv_get_string_buf_chk(&argvars[2],
- cancelreturn_buf);
- if (arg2 == NULL) {
+ const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf);
+ if (strarg2 == NULL) {
return;
}
if (inputdialog) {
- cancelreturn = arg2;
+ cancelreturn_strarg2.v_type = VAR_STRING;
+ cancelreturn_strarg2.vval.v_string = (char *)strarg2;
+ cancelreturn = &cancelreturn_strarg2;
} else {
- xp_name = arg2;
+ xp_name = strarg2;
}
}
}
@@ -6662,7 +6660,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
callback_free(&input_callback);
if (rettv->vval.v_string == NULL && cancelreturn != NULL) {
- rettv->vval.v_string = xstrdup(cancelreturn);
+ tv_copy(cancelreturn, rettv);
}
xfree(xp_arg);
@@ -7296,7 +7294,7 @@ void timer_due_cb(TimeWatcher *tw, void *data)
{
timer_T *timer = (timer_T *)data;
int save_did_emsg = did_emsg;
- int save_called_emsg = called_emsg;
+ const int called_emsg_before = called_emsg;
const bool save_ex_pressedreturn = get_pressedreturn();
if (timer->stopped || timer->paused) {
@@ -7313,19 +7311,17 @@ void timer_due_cb(TimeWatcher *tw, void *data)
argv[0].v_type = VAR_NUMBER;
argv[0].vval.v_number = timer->timer_id;
typval_T rettv = TV_INITIAL_VALUE;
- called_emsg = false;
callback_call(&timer->callback, 1, argv, &rettv);
// Handle error message
- if (called_emsg && did_emsg) {
+ if (called_emsg > called_emsg_before && did_emsg) {
timer->emsg_count++;
if (current_exception != NULL) {
discard_current_exception();
}
}
did_emsg = save_did_emsg;
- called_emsg = save_called_emsg;
set_pressedreturn(save_ex_pressedreturn);
if (timer->emsg_count >= 3) {
@@ -7754,11 +7750,14 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
}
} else if (name[0] == '\'') {
// mark
- const pos_T *const pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum);
- if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) {
+ int mname = (uint8_t)name[1];
+ const fmark_T *const fm = mark_get(curbuf, curwin, NULL, kMarkAll, mname);
+ if (fm == NULL || fm->mark.lnum <= 0) {
return NULL;
}
- pos = *pp;
+ pos = fm->mark;
+ // Vimscript behavior, only provide fnum if mark is global.
+ *ret_fnum = ASCII_ISUPPER(mname) || ascii_isdigit(mname) ? fm->fnum: *ret_fnum;
}
if (pos.lnum != 0) {
if (charcol) {
@@ -9811,7 +9810,7 @@ void func_line_start(void *cookie)
if (fp->uf_profiling && sourcing_lnum >= 1
&& sourcing_lnum <= fp->uf_lines.ga_len) {
- fp->uf_tml_idx = (int)(sourcing_lnum - 1);
+ fp->uf_tml_idx = sourcing_lnum - 1;
// Skip continuation lines.
while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL) {
fp->uf_tml_idx--;
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index f50b355045..022e2497b7 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -476,13 +476,13 @@ static buf_T *find_buffer(typval_T *avar)
if (avar->v_type == VAR_NUMBER) {
buf = buflist_findnr((int)avar->vval.v_number);
} else if (avar->v_type == VAR_STRING && avar->vval.v_string != NULL) {
- buf = buflist_findname_exp((char_u *)avar->vval.v_string);
+ buf = buflist_findname_exp(avar->vval.v_string);
if (buf == NULL) {
/* No full path name match, try a match with a URL or a "nofile"
* buffer, these don't use the full path. */
FOR_ALL_BUFFERS(bp) {
if (bp->b_fname != NULL
- && (path_with_url(bp->b_fname) || bt_nofile(bp))
+ && (path_with_url(bp->b_fname) || bt_nofilename(bp))
&& STRCMP(bp->b_fname, avar->vval.v_string) == 0) {
buf = bp;
break;
@@ -498,7 +498,7 @@ static void f_bufadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *name = (char_u *)tv_get_string(&argvars[0]);
- rettv->vval.v_number = buflist_add(*name == NUL ? NULL : name, 0);
+ rettv->vval.v_number = buflist_add(*name == NUL ? NULL : (char *)name, 0);
}
/// "bufexists(expr)" function
@@ -586,7 +586,7 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& tv_get_number_chk(&argvars[1], &error) != 0
&& !error
&& (name = tv_get_string_chk(&argvars[0])) != NULL) {
- buf = buflist_new((char_u *)name, NULL, 1, 0);
+ buf = buflist_new((char *)name, NULL, 1, 0);
}
if (buf != NULL) {
@@ -655,7 +655,7 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only)
save_cpo = p_cpo;
p_cpo = "";
- buf = buflist_findnr(buflist_findpat(name, name + STRLEN(name),
+ buf = buflist_findnr(buflist_findpat((char *)name, (char *)name + STRLEN(name),
true, false, curtab_only));
p_magic = save_magic;
@@ -1070,15 +1070,12 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[1].v_type != VAR_LIST) {
emsg(_(e_invarg));
- return;
- }
-
- const colnr_T startcol = tv_get_number_chk(&argvars[0], NULL);
- if (startcol <= 0) {
- return;
+ } else {
+ const colnr_T startcol = tv_get_number_chk(&argvars[0], NULL);
+ if (startcol > 0) {
+ set_completion(startcol - 1, argvars[1].vval.v_list);
+ }
}
-
- set_completion(startcol - 1, argvars[1].vval.v_list);
}
/// "complete_add()" function
@@ -2051,6 +2048,12 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
(void)os_can_exe(tv_get_string(&argvars[0]), &path, true);
+#ifdef BACKSLASH_IN_FILENAME
+ if (path != NULL) {
+ slash_adjust((char_u *)path);
+ }
+#endif
+
rettv->v_type = VAR_STRING;
rettv->vval.v_string = path;
}
@@ -2396,7 +2399,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
fresult = find_file_in_path_option(first ? (char_u *)fname : NULL,
first ? strlen(fname) : 0,
0, first, path,
- find_what, curbuf->b_ffname,
+ find_what, (char_u *)curbuf->b_ffname,
(find_what == FINDFILE_DIR
? (char_u *)""
: curbuf->b_p_sua));
@@ -2464,7 +2467,7 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "fnameescape({string})" function
static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_string = vim_strsave_fnameescape(tv_get_string(&argvars[0]), false);
+ rettv->vval.v_string = vim_strsave_fnameescape(tv_get_string(&argvars[0]), VSE_NONE);
rettv->v_type = VAR_STRING;
}
@@ -2574,7 +2577,7 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
}
- unsigned long count = (unsigned long)(foldend - foldstart + 1);
+ unsigned long count = (unsigned long)foldend - foldstart + 1;
txt = NGETTEXT("+-%s%3ld line: ", "+-%s%3ld lines: ", count);
r = xmalloc(STRLEN(txt)
+ STRLEN(dashes) // for %s
@@ -3308,8 +3311,8 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
[kCdScopeTabpage] = 0, // Number of tab to look at.
};
- char_u *cwd = NULL; // Current working directory to print
- char_u *from = NULL; // The original string to copy
+ char *cwd = NULL; // Current working directory to print
+ char *from = NULL; // The original string to copy
tabpage_T *tp = curtab; // The tabpage to look at.
win_T *win = curwin; // The window to look at.
@@ -3386,13 +3389,13 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
FALLTHROUGH;
case kCdScopeGlobal:
if (globaldir) { // `globaldir` is not always set.
- from = (char_u *)globaldir;
+ from = globaldir;
break;
}
FALLTHROUGH; // In global directory, just need to get OS CWD.
case kCdScopeInvalid: // If called without any arguments, get OS CWD.
- if (os_dirname(cwd, MAXPATHL) == FAIL) {
- from = (char_u *)""; // Return empty string on failure.
+ if (os_dirname((char_u *)cwd, MAXPATHL) == FAIL) {
+ from = ""; // Return empty string on failure.
}
}
@@ -3400,7 +3403,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
STRLCPY(cwd, from, MAXPATHL);
}
- rettv->vval.v_string = (char *)vim_strsave(cwd);
+ rettv->vval.v_string = xstrdup(cwd);
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(rettv->vval.v_string);
#endif
@@ -3899,15 +3902,14 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
typval_T argv = TV_INITIAL_VALUE;
typval_T exprval = TV_INITIAL_VALUE;
bool error = false;
- int save_called_emsg = called_emsg;
- called_emsg = false;
+ const int called_emsg_before = called_emsg;
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, timeout,
eval_expr_typval(&expr, &argv, 0, &exprval) != OK
|| tv_get_number_chk(&exprval, &error)
- || called_emsg || error || got_int);
+ || called_emsg > called_emsg_before || error || got_int);
- if (called_emsg || error) {
+ if (called_emsg > called_emsg_before || error) {
rettv->vval.v_number = -3;
} else if (got_int) {
got_int = false;
@@ -3917,8 +3919,6 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 0;
}
- called_emsg = save_called_emsg;
-
// Stop dummy timer
time_watcher_stop(tw);
time_watcher_close(tw, dummy_timer_close_cb);
@@ -5968,7 +5968,7 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (*path_tail(dir) == NUL) {
// Remove trailing slashes.
- *path_tail_with_sep((char_u *)dir) = NUL;
+ *path_tail_with_sep((char *)dir) = NUL;
}
if (argvars[1].v_type != VAR_UNKNOWN) {
@@ -6366,20 +6366,17 @@ static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "prompt_setprompt({buffer}, {text})" function
static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- buf_T *buf;
- const char_u *text;
-
if (check_secure()) {
return;
}
- buf = tv_get_buf(&argvars[0], false);
+ buf_T *buf = tv_get_buf(&argvars[0], false);
if (buf == NULL) {
return;
}
- text = (const char_u *)tv_get_string(&argvars[1]);
+ const char *text = tv_get_string(&argvars[1]);
xfree(buf->b_prompt_text);
- buf->b_prompt_text = vim_strsave(text);
+ buf->b_prompt_text = xstrdup(text);
}
/// "pum_getpos()" function
@@ -7341,7 +7338,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (!has_trailing_pathsep) {
q = p + strlen(p);
if (after_pathsep(p, q)) {
- *path_tail_with_sep((char_u *)p) = NUL;
+ *path_tail_with_sep(p) = NUL;
}
}
@@ -8521,7 +8518,7 @@ static void set_position(typval_T *argvars, typval_T *rettv, bool charpos)
rettv->vval.v_number = 0;
} else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) {
// set mark
- if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) {
+ if (setmark_pos((uint8_t)name[1], &pos, fnum, NULL) == OK) {
rettv->vval.v_number = 0;
}
} else {
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 97726da5f4..e19cf411c0 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1434,7 +1434,7 @@ dictitem_T *tv_dict_item_copy(dictitem_T *const di)
void tv_dict_item_remove(dict_T *const dict, dictitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
- hashitem_T *const hi = hash_find(&dict->dv_hashtab, item->di_key);
+ hashitem_T *const hi = hash_find(&dict->dv_hashtab, (char *)item->di_key);
if (HASHITEM_EMPTY(hi)) {
semsg(_(e_intern2), "tv_dict_item_remove()");
} else {
@@ -1567,7 +1567,7 @@ dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, const ptr
return NULL;
}
hashitem_T *const hi = (len < 0
- ? hash_find(&d->dv_hashtab, (const char_u *)key)
+ ? hash_find(&d->dv_hashtab, key)
: hash_find_len(&d->dv_hashtab, key, (size_t)len));
if (HASHITEM_EMPTY(hi)) {
return NULL;
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 8646520ec3..c2579944e4 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -547,9 +547,7 @@ static char_u *fname_trans_sid(const char_u *const name, char_u *const fname_buf
/// @return NULL for unknown function.
ufunc_T *find_func(const char_u *name)
{
- hashitem_T *hi;
-
- hi = hash_find(&func_hashtab, name);
+ hashitem_T *hi = hash_find(&func_hashtab, (char *)name);
if (!HASHITEM_EMPTY(hi)) {
return HI2UF(hi);
}
@@ -724,7 +722,7 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force)
/// @return true if the entry was deleted, false if it wasn't found.
static bool func_remove(ufunc_T *fp)
{
- hashitem_T *hi = hash_find(&func_hashtab, UF2HIKEY(fp));
+ hashitem_T *hi = hash_find(&func_hashtab, (char *)UF2HIKEY(fp));
if (!HASHITEM_EMPTY(hi)) {
hash_remove(&func_hashtab, hi);
@@ -1045,7 +1043,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
char *s = tofree;
char buf[MSG_BUF_LEN];
if (vim_strsize(s) > MSG_BUF_CLEN) {
- trunc_string((char_u *)s, (char_u *)buf, MSG_BUF_CLEN, sizeof(buf));
+ trunc_string(s, buf, MSG_BUF_CLEN, sizeof(buf));
s = buf;
}
msg_puts(s);
@@ -1159,7 +1157,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
emsg_off--;
if (s != NULL) {
if (vim_strsize(s) > MSG_BUF_CLEN) {
- trunc_string((char_u *)s, (char_u *)buf, MSG_BUF_CLEN, MSG_BUF_LEN);
+ trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
s = buf;
}
smsg(_("%s returning %s"), sourcing_name, s);
@@ -1976,7 +1974,7 @@ void ex_function(exarg_T *eap)
--todo;
fp = HI2UF(hi);
if (!isdigit(*fp->uf_name)
- && vim_regexec(&regmatch, fp->uf_name, 0)) {
+ && vim_regexec(&regmatch, (char *)fp->uf_name, 0)) {
list_func_head(fp, false, false);
}
}
@@ -2537,7 +2535,7 @@ void ex_function(exarg_T *eap)
// insert the new function in the function list
STRCPY(fp->uf_name, name);
if (overwrite) {
- hi = hash_find(&func_hashtab, name);
+ hi = hash_find(&func_hashtab, (char *)name);
hi->hi_key = UF2HIKEY(fp);
} else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
xfree(fp);
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c
index f40573a0bc..1b5cc23b09 100644
--- a/src/nvim/event/loop.c
+++ b/src/nvim/event/loop.c
@@ -27,6 +27,7 @@ void loop_init(Loop *loop, void *data)
uv_signal_init(&loop->uv, &loop->children_watcher);
uv_timer_init(&loop->uv, &loop->children_kill_timer);
uv_timer_init(&loop->uv, &loop->poll_timer);
+ uv_timer_init(&loop->uv, &loop->exit_delay_timer);
loop->poll_timer.data = xmalloc(sizeof(bool)); // "timeout expired" flag
}
@@ -136,6 +137,7 @@ bool loop_close(Loop *loop, bool wait)
uv_close((uv_handle_t *)&loop->children_watcher, NULL);
uv_close((uv_handle_t *)&loop->children_kill_timer, NULL);
uv_close((uv_handle_t *)&loop->poll_timer, timer_close_cb);
+ uv_close((uv_handle_t *)&loop->exit_delay_timer, NULL);
uv_close((uv_handle_t *)&loop->async, NULL);
uint64_t start = wait ? os_hrtime() : 0;
bool didstop = false;
diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h
index c0c5bc527f..65980c6c05 100644
--- a/src/nvim/event/loop.h
+++ b/src/nvim/event/loop.h
@@ -36,6 +36,8 @@ typedef struct loop {
// generic timer, used by loop_poll_events()
uv_timer_t poll_timer;
+ uv_timer_t exit_delay_timer;
+
uv_async_t async;
uv_mutex_t mutex;
int recursive;
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index 1ec11f1eb6..e029f778f6 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -386,11 +386,13 @@ static void process_close_handles(void **argv)
{
Process *proc = argv[0];
+ exit_need_delay++;
flush_stream(proc, &proc->out);
flush_stream(proc, &proc->err);
process_close_streams(proc);
process_close(proc);
+ exit_need_delay--;
}
static void on_process_exit(Process *proc)
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 8016c69bcb..28e1893b31 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -560,7 +560,7 @@ void ex_sort(exarg_T *eap)
start_col = 0;
end_col = len;
- if (regmatch.regprog != NULL && vim_regexec(&regmatch, (char_u *)s, 0)) {
+ if (regmatch.regprog != NULL && vim_regexec(&regmatch, s, 0)) {
if (sort_rx) {
start_col = (colnr_T)(regmatch.startp[0] - (char_u *)s);
end_col = (colnr_T)(regmatch.endp[0] - (char_u *)s);
@@ -1732,19 +1732,19 @@ int rename_buffer(char *new_fname)
* But don't set the alternate file name if the buffer didn't have a
* name.
*/
- fname = (char *)curbuf->b_ffname;
- sfname = (char *)curbuf->b_sfname;
+ fname = curbuf->b_ffname;
+ sfname = curbuf->b_sfname;
xfname = curbuf->b_fname;
curbuf->b_ffname = NULL;
curbuf->b_sfname = NULL;
if (setfname(curbuf, new_fname, NULL, true) == FAIL) {
- curbuf->b_ffname = (char_u *)fname;
- curbuf->b_sfname = (char_u *)sfname;
+ curbuf->b_ffname = fname;
+ curbuf->b_sfname = sfname;
return FAIL;
}
curbuf->b_flags |= BF_NOTEDITED;
if (xfname != NULL && *xfname != NUL) {
- buf = buflist_new((char_u *)fname, (char_u *)xfname, curwin->w_cursor.lnum, 0);
+ buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0);
if (buf != NULL && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
curwin->w_alt_fnum = buf->b_fnum;
}
@@ -1842,7 +1842,7 @@ int do_write(exarg_T *eap)
if (free_fname != NULL) {
ffname = free_fname;
}
- other = otherfile((char_u *)ffname);
+ other = otherfile(ffname);
}
/*
@@ -1851,9 +1851,9 @@ int do_write(exarg_T *eap)
if (other) {
if (vim_strchr(p_cpo, CPO_ALTWRITE) != NULL
|| eap->cmdidx == CMD_saveas) {
- alt_buf = setaltfname((char_u *)ffname, (char_u *)fname, (linenr_T)1);
+ alt_buf = setaltfname(ffname, fname, (linenr_T)1);
} else {
- alt_buf = buflist_findname((char_u *)ffname);
+ alt_buf = buflist_findname(ffname);
}
if (alt_buf != NULL && alt_buf->b_ml.ml_mfp != NULL) {
// Overwriting a file that is loaded in another buffer is not a
@@ -1873,7 +1873,7 @@ int do_write(exarg_T *eap)
}
if (!other) {
- ffname = (char *)curbuf->b_ffname;
+ ffname = curbuf->b_ffname;
fname = curbuf->b_fname;
// Not writing the whole file is only allowed with '!'.
if ((eap->line1 != 1
@@ -1913,12 +1913,12 @@ int do_write(exarg_T *eap)
fname = alt_buf->b_fname;
alt_buf->b_fname = curbuf->b_fname;
curbuf->b_fname = fname;
- fname = (char *)alt_buf->b_ffname;
+ fname = alt_buf->b_ffname;
alt_buf->b_ffname = curbuf->b_ffname;
- curbuf->b_ffname = (char_u *)fname;
- fname = (char *)alt_buf->b_sfname;
+ curbuf->b_ffname = fname;
+ fname = alt_buf->b_sfname;
alt_buf->b_sfname = curbuf->b_sfname;
- curbuf->b_sfname = (char_u *)fname;
+ curbuf->b_sfname = fname;
buf_name_changed(curbuf);
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf);
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, alt_buf);
@@ -1942,7 +1942,7 @@ int do_write(exarg_T *eap)
// Autocommands may have changed buffer names, esp. when
// 'autochdir' is set.
- fname = (char *)curbuf->b_sfname;
+ fname = curbuf->b_sfname;
}
name_was_missing = curbuf->b_ffname == NULL;
@@ -1980,17 +1980,14 @@ theend:
/// @return OK if it's OK, FAIL if it is not.
int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int other)
{
- /*
- * write to other file or b_flags set or not writing the whole file:
- * overwriting only allowed with '!'
- */
+ // Write to another file or b_flags set or not writing the whole file:
+ // overwriting only allowed with '!'
if ((other
|| (buf->b_flags & BF_NOTEDITED)
|| ((buf->b_flags & BF_NEW)
&& vim_strchr(p_cpo, CPO_OVERNEW) == NULL)
|| (buf->b_flags & BF_READERR))
&& !p_wa
- && !bt_nofile(buf)
&& os_path_exists((char_u *)ffname)) {
if (!eap->forceit && !eap->append) {
#ifdef UNIX
@@ -2031,7 +2028,7 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth
} else {
dir = xmalloc(MAXPATHL);
p = (char *)p_dir;
- copy_option_part((char_u **)&p, (char_u *)dir, MAXPATHL, ",");
+ copy_option_part(&p, dir, MAXPATHL, ",");
}
swapname = (char *)makeswapname((char_u *)fname, (char_u *)ffname, curbuf, (char_u *)dir);
xfree(dir);
@@ -2112,7 +2109,7 @@ void do_wqall(exarg_T *eap)
semsg(_("E141: No file name for buffer %" PRId64), (int64_t)buf->b_fnum);
error++;
} else if (check_readonly(&eap->forceit, buf)
- || check_overwrite(eap, buf, buf->b_fname, (char *)buf->b_ffname, false) == FAIL) {
+ || check_overwrite(eap, buf, buf->b_fname, buf->b_ffname, false) == FAIL) {
error++;
} else {
bufref_T bufref;
@@ -2155,8 +2152,8 @@ static int check_readonly(int *forceit, buf_T *buf)
// Handle a file being readonly when the 'readonly' option is set or when
// the file exists and permissions are read-only.
if (!*forceit && (buf->b_p_ro
- || (os_path_exists(buf->b_ffname)
- && !os_file_is_writable((char *)buf->b_ffname)))) {
+ || (os_path_exists((char_u *)buf->b_ffname)
+ && !os_file_is_writable(buf->b_ffname)))) {
if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && buf->b_fname != NULL) {
char buff[DIALOG_MSG_SIZE];
@@ -2218,7 +2215,7 @@ int getfile(int fnum, char *ffname_arg, char *sfname_arg, int setpm, linenr_T ln
if (fnum == 0) {
// make ffname full path, set sfname
fname_expand(curbuf, &ffname, &sfname);
- other = otherfile((char_u *)ffname);
+ other = otherfile(ffname);
free_me = ffname; // has been allocated, free() later
} else {
other = (fnum != curbuf->b_fnum);
@@ -2339,7 +2336,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
}
#ifdef USE_FNAME_CASE
if (sfname != NULL) {
- path_fix_case((char_u *)sfname); // set correct case for sfname
+ path_fix_case(sfname); // set correct case for sfname
}
#endif
@@ -2354,14 +2351,14 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
other_file = false;
} else {
if (*ffname == NUL) { // re-edit with same file name
- ffname = (char *)curbuf->b_ffname;
+ ffname = curbuf->b_ffname;
sfname = curbuf->b_fname;
}
free_fname = fix_fname(ffname); // may expand to full path name
if (free_fname != NULL) {
ffname = free_fname;
}
- other_file = otherfile((char_u *)ffname);
+ other_file = otherfile(ffname);
}
}
@@ -2384,7 +2381,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
| ((flags & ECMD_FORCEIT) ? CCGD_FORCEIT : 0)
| (eap == NULL ? 0 : CCGD_EXCMD))) {
if (fnum == 0 && other_file && ffname != NULL) {
- (void)setaltfname((char_u *)ffname, (char_u *)sfname, newlnum < 0 ? 0 : newlnum);
+ (void)setaltfname(ffname, sfname, newlnum < 0 ? 0 : newlnum);
}
goto theend;
}
@@ -2415,6 +2412,8 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
* Otherwise we re-use the current buffer.
*/
if (other_file) {
+ const int prev_alt_fnum = curwin->w_alt_fnum;
+
if (!(flags & (ECMD_ADDBUF | ECMD_ALTBUF))) {
if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
curwin->w_alt_fnum = curbuf->b_fnum;
@@ -2441,13 +2440,13 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// Add BLN_NOCURWIN to avoid a new wininfo items are associated
// with the current window.
const buf_T *const newbuf
- = buflist_new((char_u *)ffname, (char_u *)sfname, tlnum, BLN_LISTED | BLN_NOCURWIN);
+ = buflist_new(ffname, sfname, tlnum, BLN_LISTED | BLN_NOCURWIN);
if (newbuf != NULL && (flags & ECMD_ALTBUF)) {
curwin->w_alt_fnum = newbuf->b_fnum;
}
goto theend;
}
- buf = buflist_new((char_u *)ffname, (char_u *)sfname, 0L,
+ buf = buflist_new(ffname, sfname, 0L,
BLN_CURBUF | (flags & ECMD_SET_HELP ? 0 : BLN_LISTED));
// Autocmds may change curwin and curbuf.
if (oldwin != NULL) {
@@ -2458,6 +2457,10 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
if (buf == NULL) {
goto theend;
}
+ if (curwin->w_alt_fnum == buf->b_fnum && prev_alt_fnum != 0) {
+ // reusing the buffer, keep the old alternate file
+ curwin->w_alt_fnum = prev_alt_fnum;
+ }
if (buf->b_ml.ml_mfp == NULL) {
// No memfile yet.
oldbuf = false;
@@ -2480,7 +2483,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// May jump to last used line number for a loaded buffer or when asked
// for explicitly
if ((oldbuf && newlnum == ECMD_LASTL) || newlnum == ECMD_LAST) {
- pos = buflist_findfpos(buf);
+ pos = &buflist_findfmark(buf)->mark;
newlnum = pos->lnum;
solcol = pos->col;
}
@@ -3651,6 +3654,13 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
sub_needs_free = cmdpreview && sub != source;
}
+ bool cmdheight0 = p_ch < 1 && !ui_has(kUIMessages);
+ if (cmdheight0) {
+ // If cmdheight is 0, cmdheight must be set to 1 when we enter command line.
+ set_option_value("ch", 1L, NULL, 0);
+ redraw_statuslines();
+ }
+
// Check for a match on each line.
// If preview: limit to max('cmdwinheight', viewport).
linenr_T line2 = eap->line2;
@@ -3861,6 +3871,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
curwin->w_cursor.col = 0;
}
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec);
+ curwin->w_cursor.col = regmatch.startpos[0].col;
if (subflags.do_number || curwin->w_p_nu) {
int numw = number_width(curwin) + 1;
sc += numw;
@@ -3870,7 +3881,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
prompt = xmallocz((size_t)ec + 1);
memset(prompt, ' ', (size_t)sc);
memset(prompt + sc, '^', (size_t)(ec - sc) + 1);
- resp = getcmdline_prompt((char)(-1), prompt, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE);
+ resp = getcmdline_prompt(-1, prompt, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE);
msg_putchar('\n');
xfree(prompt);
if (resp != NULL) {
@@ -4461,6 +4472,11 @@ skip:
}
}
+ if (cmdheight0) {
+ // Restore cmdheight
+ set_option_value("ch", 0L, NULL, 0);
+ }
+
kv_destroy(preview_lines.subresults);
return retv;
#undef ADJUST_SUB_FIRSTLNUM
@@ -4843,8 +4859,7 @@ void ex_help(exarg_T *eap)
* Re-use an existing help window or open a new one.
* Always open a new one for ":tab help".
*/
- if (!bt_help(curwin->w_buffer)
- || cmdmod.cmod_tab != 0) {
+ if (!bt_help(curwin->w_buffer) || cmdmod.cmod_tab != 0) {
if (cmdmod.cmod_tab != 0) {
wp = NULL;
} else {
@@ -5007,7 +5022,15 @@ static int help_compare(const void *s1, const void *s2)
p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
- return strcmp(p1, p2);
+
+ // Compare by help heuristic number first.
+ int cmp = strcmp(p1, p2);
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ // Compare by strings as tie-breaker when same heuristic number.
+ return strcmp(*(char **)s1, *(char **)s2);
}
/// Find all help tags matching "arg", sort them and return in matches[], with
@@ -5368,7 +5391,7 @@ void fix_help_buffer(void)
// $VIMRUNTIME.
char *p = (char *)p_rtp;
while (*p != NUL) {
- copy_option_part((char_u **)&p, NameBuff, MAXPATHL, ",");
+ copy_option_part(&p, (char *)NameBuff, MAXPATHL, ",");
char *const rt = vim_getenv("VIMRUNTIME");
if (rt != NULL
&& path_full_compare(rt, (char *)NameBuff, false, true) != kEqualFiles) {
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index b18bdefc2a..a5ba5e0b30 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -26,6 +26,7 @@ local SBOXOK = 0x40000
local CMDWIN = 0x80000
local MODIFY = 0x100000
local FLAGS = 0x200000
+local LOCK_OK = 0x1000000
local PREVIEW = 0x8000000
local FILES = bit.bor(XFILE, EXTRA)
local WORD1 = bit.bor(EXTRA, NOSPC)
@@ -41,19 +42,19 @@ module.flags = {
module.cmds = {
{
command='append',
- flags=bit.bor(BANG, RANGE, ZEROR, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(BANG, RANGE, ZEROR, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_append',
},
{
command='abbreviate',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='abclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abclear',
},
@@ -71,13 +72,13 @@ module.cmds = {
},
{
command='amenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='anoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -131,25 +132,25 @@ module.cmds = {
},
{
command='ascii',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='do_ascii',
},
{
command='autocmd',
- flags=bit.bor(BANG, EXTRA, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_autocmd',
},
{
command='augroup',
- flags=bit.bor(BANG, WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_autocmd',
},
{
command='aunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
@@ -173,13 +174,13 @@ module.cmds = {
},
{
command='badd',
- flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN),
+ flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_edit',
},
{
command='balt',
- flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN),
+ flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_edit',
},
@@ -191,7 +192,7 @@ module.cmds = {
},
{
command='behave',
- flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_behave',
},
@@ -245,37 +246,37 @@ module.cmds = {
},
{
command='break',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_break',
},
{
command='breakadd',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_breakadd',
},
{
command='breakdel',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_breakdel',
},
{
command='breaklist',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_breaklist',
},
{
command='browse',
- flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_wrongmodifier',
},
{
command='buffers',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='buflist_list',
},
@@ -299,7 +300,7 @@ module.cmds = {
},
{
command='change',
- flags=bit.bor(BANG, WHOLEFOLD, RANGE, COUNT, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(BANG, WHOLEFOLD, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_change',
},
@@ -317,13 +318,13 @@ module.cmds = {
},
{
command='cabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='cabclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abclear',
},
@@ -359,13 +360,13 @@ module.cmds = {
},
{
command='call',
- flags=bit.bor(RANGE, NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_call',
},
{
command='catch',
- flags=bit.bor(EXTRA, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_catch',
},
@@ -407,7 +408,7 @@ module.cmds = {
},
{
command='cd',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
@@ -419,7 +420,7 @@ module.cmds = {
},
{
command='center',
- flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
+ flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_align',
},
@@ -469,13 +470,13 @@ module.cmds = {
},
{
command='chdir',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
{
command='changes',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_changes',
},
@@ -487,7 +488,7 @@ module.cmds = {
},
{
command='checkpath',
- flags=bit.bor(TRLBAR, BANG, CMDWIN),
+ flags=bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_checkpath',
},
@@ -505,7 +506,7 @@ module.cmds = {
},
{
command='clist',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='qf_list',
},
@@ -517,31 +518,31 @@ module.cmds = {
},
{
command='close',
- flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_WINDOWS',
func='ex_close',
},
{
command='clearjumps',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_clearjumps',
},
{
command='cmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='cmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='cmenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -565,25 +566,25 @@ module.cmds = {
},
{
command='cnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='cnoreabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='cnoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='copy',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_copymove',
},
@@ -595,43 +596,43 @@ module.cmds = {
},
{
command='colorscheme',
- flags=bit.bor(WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_colorscheme',
},
{
command='command',
- flags=bit.bor(EXTRA, BANG, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, BANG, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_command',
},
{
command='comclear',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_comclear',
},
{
command='compiler',
- flags=bit.bor(BANG, TRLBAR, WORD1, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, WORD1, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_compiler',
},
{
command='continue',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_continue',
},
{
command='confirm',
- flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_wrongmodifier',
},
{
command='const',
- flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_const',
},
@@ -679,19 +680,19 @@ module.cmds = {
},
{
command='cunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='cunabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='cunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
@@ -703,43 +704,43 @@ module.cmds = {
},
{
command='delete',
- flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_operators',
},
{
command='delmarks',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_delmarks',
},
{
command='debug',
- flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_debug',
},
{
command='debuggreedy',
- flags=bit.bor(RANGE, ZEROR, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_debuggreedy',
},
{
command='delcommand',
- flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_delcommand',
},
{
command='delfunction',
- flags=bit.bor(BANG, NEEDARG, WORD1, CMDWIN),
+ flags=bit.bor(BANG, NEEDARG, WORD1, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_delfunction',
},
{
command='display',
- flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_display',
},
@@ -787,7 +788,7 @@ module.cmds = {
},
{
command='digraphs',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_digraphs',
},
@@ -799,19 +800,19 @@ module.cmds = {
},
{
command='dlist',
- flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN),
+ flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_findpat',
},
{
command='doautocmd',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_doautocmd',
},
{
command='doautoall',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_doautoall',
},
@@ -823,7 +824,7 @@ module.cmds = {
},
{
command='dsearch',
- flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN),
+ flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_findpat',
},
@@ -841,85 +842,85 @@ module.cmds = {
},
{
command='earlier',
- flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN),
+ flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_later',
},
{
command='echo',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_echo',
},
{
command='echoerr',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_execute',
},
{
command='echohl',
- flags=bit.bor(EXTRA, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_echohl',
},
{
command='echomsg',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_execute',
},
{
command='echon',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_echo',
},
{
command='else',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_else',
},
{
command='elseif',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_else',
},
{
command='emenu',
- flags=bit.bor(NEEDARG, EXTRA, TRLBAR, NOTRLCOM, RANGE, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, TRLBAR, NOTRLCOM, RANGE, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_emenu',
},
{
command='endif',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_endif',
},
{
command='endfunction',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_endfunction',
},
{
command='endfor',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_endwhile',
},
{
command='endtry',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_endtry',
},
{
command='endwhile',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_endwhile',
},
@@ -931,7 +932,7 @@ module.cmds = {
},
{
command='eval',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_eval',
},
@@ -943,13 +944,13 @@ module.cmds = {
},
{
command='execute',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_execute',
},
{
command='exit',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_exit',
},
@@ -967,13 +968,13 @@ module.cmds = {
},
{
command='files',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='buflist_list',
},
{
command='filetype',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_filetype',
},
@@ -991,13 +992,13 @@ module.cmds = {
},
{
command='finally',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_finally',
},
{
command='finish',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_finish',
},
@@ -1009,13 +1010,13 @@ module.cmds = {
},
{
command='fold',
- flags=bit.bor(RANGE, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_fold',
},
{
command='foldclose',
- flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_foldopen',
},
@@ -1033,31 +1034,31 @@ module.cmds = {
},
{
command='foldopen',
- flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_foldopen',
},
{
command='for',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_while',
},
{
command='function',
- flags=bit.bor(EXTRA, BANG, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, BANG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_function',
},
{
command='global',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, DFLALL, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, DFLALL, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_global',
},
{
command='goto',
- flags=bit.bor(RANGE, COUNT, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, COUNT, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_goto',
},
@@ -1075,13 +1076,13 @@ module.cmds = {
},
{
command='gui',
- flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_nogui',
},
{
command='gvim',
- flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_nogui',
},
@@ -1105,7 +1106,7 @@ module.cmds = {
},
{
command='helptags',
- flags=bit.bor(NEEDARG, FILES, TRLBAR, CMDWIN),
+ flags=bit.bor(NEEDARG, FILES, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_helptags',
},
@@ -1117,7 +1118,7 @@ module.cmds = {
},
{
command='highlight',
- flags=bit.bor(BANG, EXTRA, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_highlight',
},
@@ -1129,31 +1130,31 @@ module.cmds = {
},
{
command='history',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_history',
},
{
command='insert',
- flags=bit.bor(BANG, RANGE, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(BANG, RANGE, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_append',
},
{
command='iabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='iabclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abclear',
},
{
command='if',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_if',
},
@@ -1165,55 +1166,55 @@ module.cmds = {
},
{
command='ilist',
- flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN),
+ flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_findpat',
},
{
command='imap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='imapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='imenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='inoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='inoreabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='inoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='intro',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_intro',
},
{
command='isearch',
- flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN),
+ flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_findpat',
},
@@ -1225,37 +1226,37 @@ module.cmds = {
},
{
command='iunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='iunabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='iunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='join',
- flags=bit.bor(BANG, RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(BANG, RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_join',
},
{
command='jumps',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_jumps',
},
{
command='k',
- flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_mark',
},
@@ -1285,7 +1286,7 @@ module.cmds = {
},
{
command='list',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_print',
},
@@ -1315,7 +1316,7 @@ module.cmds = {
},
{
command='language',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_language',
},
@@ -1345,7 +1346,7 @@ module.cmds = {
},
{
command='later',
- flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN),
+ flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_later',
},
@@ -1375,13 +1376,13 @@ module.cmds = {
},
{
command='lcd',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
{
command='lchdir',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
@@ -1405,7 +1406,7 @@ module.cmds = {
},
{
command='left',
- flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
+ flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_align',
},
@@ -1417,7 +1418,7 @@ module.cmds = {
},
{
command='let',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_let',
},
@@ -1503,19 +1504,19 @@ module.cmds = {
},
{
command='llist',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='qf_list',
},
{
command='lmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='lmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
@@ -1527,7 +1528,7 @@ module.cmds = {
},
{
command='lnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
@@ -1557,7 +1558,7 @@ module.cmds = {
},
{
command='loadkeymap',
- flags=bit.bor(CMDWIN),
+ flags=bit.bor(CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_loadkeymap',
},
@@ -1569,7 +1570,7 @@ module.cmds = {
},
{
command='lockvar',
- flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_lockvar',
},
@@ -1611,37 +1612,37 @@ module.cmds = {
},
{
command='lunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='lua',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_lua',
},
{
command='luado',
- flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_luado',
},
{
command='luafile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_luafile',
},
{
command='lvimgrep',
- flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE),
+ flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_vimgrep',
},
{
command='lvimgrepadd',
- flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE),
+ flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_vimgrep',
},
@@ -1653,19 +1654,19 @@ module.cmds = {
},
{
command='ls',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='buflist_list',
},
{
command='move',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_copymove',
},
{
command='mark',
- flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_mark',
},
@@ -1677,49 +1678,49 @@ module.cmds = {
},
{
command='map',
- flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='mapclear',
- flags=bit.bor(EXTRA, BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='marks',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_marks',
},
{
command='match',
- flags=bit.bor(RANGE, EXTRA, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_match',
},
{
command='menu',
- flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='menutranslate',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menutranslate',
},
{
command='messages',
- flags=bit.bor(EXTRA, TRLBAR, RANGE, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, RANGE, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_messages',
},
{
command='mkexrc',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mkrc',
},
@@ -1737,7 +1738,7 @@ module.cmds = {
},
{
command='mkvimrc',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mkrc',
},
@@ -1749,19 +1750,19 @@ module.cmds = {
},
{
command='mode',
- flags=bit.bor(WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mode',
},
{
command='mzscheme',
- flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, SBOXOK),
+ flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, LOCK_OK, SBOXOK),
addr_type='ADDR_LINES',
func='ex_script_ni',
},
{
command='mzfile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_ni',
},
@@ -1779,37 +1780,37 @@ module.cmds = {
},
{
command='nmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='nmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='nmenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='nnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='nnoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='noremap',
- flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
@@ -1821,19 +1822,19 @@ module.cmds = {
},
{
command='nohlsearch',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_nohlsearch',
},
{
command='noreabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='noremenu',
- flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -1845,49 +1846,49 @@ module.cmds = {
},
{
command='normal',
- flags=bit.bor(RANGE, BANG, EXTRA, NEEDARG, NOTRLCOM, CTRLV, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, BANG, EXTRA, NEEDARG, NOTRLCOM, CTRLV, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_normal',
},
{
command='number',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_print',
},
{
command='nunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='nunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='oldfiles',
- flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_oldfiles',
},
{
command='omap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='omapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='omenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -1899,13 +1900,13 @@ module.cmds = {
},
{
command='onoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='onoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -1917,37 +1918,37 @@ module.cmds = {
},
{
command='ounmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='ounmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='ownsyntax',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_ownsyntax',
},
{
command='print',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, SBOXOK),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, SBOXOK),
addr_type='ADDR_LINES',
func='ex_print',
},
{
command='packadd',
- flags=bit.bor(BANG, FILE1, NEEDARG, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, FILE1, NEEDARG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_packadd',
},
{
command='packloadall',
- flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_packloadall',
},
@@ -1959,19 +1960,19 @@ module.cmds = {
},
{
command='perl',
- flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_perl',
},
{
command='perldo',
- flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_perldo',
},
{
command='perlfile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_perlfile',
},
@@ -1989,9 +1990,9 @@ module.cmds = {
},
{
command='popup',
- flags=bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
- func='ex_ni',
+ func='ex_popup',
},
{
command='ppop',
@@ -2013,13 +2014,13 @@ module.cmds = {
},
{
command='profile',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_profile',
},
{
command='profdel',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_breakdel',
},
@@ -2085,85 +2086,85 @@ module.cmds = {
},
{
command='put',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, REGSTR, TRLBAR, ZEROR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, REGSTR, TRLBAR, ZEROR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_put',
},
{
command='pwd',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_pwd',
},
{
command='python',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_python3',
},
{
command='pydo',
- flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_pydo3',
},
{
command='pyfile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_py3file',
},
{
command='py3',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_python3',
},
{
command='py3do',
- flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_pydo3',
},
{
command='python3',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_python3',
},
{
command='py3file',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_py3file',
},
{
command='pyx',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_python3',
},
{
command='pyxdo',
- flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_pydo3',
},
{
command='pythonx',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_python3',
},
{
command='pyxfile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_py3file',
},
{
command='quit',
- flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_WINDOWS',
func='ex_quit',
},
@@ -2175,13 +2176,13 @@ module.cmds = {
},
{
command='qall',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_quit_all',
},
{
command='read',
- flags=bit.bor(BANG, RANGE, WHOLEFOLD, FILE1, ARGOPT, TRLBAR, ZEROR, CMDWIN, MODIFY),
+ flags=bit.bor(BANG, RANGE, WHOLEFOLD, FILE1, ARGOPT, TRLBAR, ZEROR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_read',
},
@@ -2193,55 +2194,55 @@ module.cmds = {
},
{
command='redo',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_redo',
},
{
command='redir',
- flags=bit.bor(BANG, FILES, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILES, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_redir',
},
{
command='redraw',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_redraw',
},
{
command='redrawstatus',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_redrawstatus',
},
{
command='redrawtabline',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_redrawtabline',
},
{
command='registers',
- flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_display',
},
{
command='resize',
- flags=bit.bor(RANGE, TRLBAR, WORD1, CMDWIN),
+ flags=bit.bor(RANGE, TRLBAR, WORD1, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_resize',
},
{
command='retab',
- flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, DFLALL, BANG, WORD1, CMDWIN, MODIFY),
+ flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, DFLALL, BANG, WORD1, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_retab',
},
{
command='return',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_return',
},
@@ -2253,7 +2254,7 @@ module.cmds = {
},
{
command='right',
- flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
+ flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_align',
},
@@ -2265,13 +2266,13 @@ module.cmds = {
},
{
command='rshada',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_shada',
},
{
command='runtime',
- flags=bit.bor(BANG, NEEDARG, FILES, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, NEEDARG, FILES, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_runtime',
},
@@ -2283,31 +2284,31 @@ module.cmds = {
},
{
command='ruby',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_ruby',
},
{
command='rubydo',
- flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_rubydo',
},
{
command='rubyfile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_rubyfile',
},
{
command='rviminfo',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_shada',
},
{
command='substitute',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, PREVIEW),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW),
addr_type='ADDR_LINES',
func='ex_substitute',
preview_func='ex_substitute_preview',
@@ -2338,7 +2339,7 @@ module.cmds = {
},
{
command='saveas',
- flags=bit.bor(BANG, FILE1, ARGOPT, CMDWIN, TRLBAR),
+ flags=bit.bor(BANG, FILE1, ARGOPT, CMDWIN, LOCK_OK, TRLBAR),
addr_type='ADDR_NONE',
func='ex_write',
},
@@ -2398,13 +2399,13 @@ module.cmds = {
},
{
command='scriptnames',
- flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_scriptnames',
},
{
command='scriptencoding',
- flags=bit.bor(WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_scriptencoding',
},
@@ -2416,25 +2417,25 @@ module.cmds = {
},
{
command='set',
- flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
{
command='setfiletype',
- flags=bit.bor(TRLBAR, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(TRLBAR, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_setfiletype',
},
{
command='setglobal',
- flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
{
command='setlocal',
- flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
@@ -2452,25 +2453,25 @@ module.cmds = {
},
{
command='simalt',
- flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_ni',
},
{
command='sign',
- flags=bit.bor(NEEDARG, RANGE, EXTRA, CMDWIN),
+ flags=bit.bor(NEEDARG, RANGE, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_sign',
},
{
command='silent',
- flags=bit.bor(NEEDARG, EXTRA, BANG, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, BANG, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_wrongmodifier',
},
{
command='sleep',
- flags=bit.bor(BANG, RANGE, COUNT, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, COUNT, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_sleep',
},
@@ -2482,26 +2483,26 @@ module.cmds = {
},
{
command='smagic',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, PREVIEW),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW),
addr_type='ADDR_LINES',
func='ex_submagic',
preview_func='ex_submagic_preview',
},
{
command='smap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='smapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='smenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -2513,26 +2514,26 @@ module.cmds = {
},
{
command='snomagic',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, PREVIEW),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW),
addr_type='ADDR_LINES',
func='ex_submagic',
preview_func='ex_submagic_preview',
},
{
command='snoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='snoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='source',
- flags=bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, FILE1, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, FILE1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_source',
},
@@ -2604,7 +2605,7 @@ module.cmds = {
},
{
command='stop',
- flags=bit.bor(TRLBAR, BANG, CMDWIN),
+ flags=bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_stop',
},
@@ -2616,25 +2617,25 @@ module.cmds = {
},
{
command='startinsert',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_startinsert',
},
{
command='startgreplace',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_startinsert',
},
{
command='startreplace',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_startinsert',
},
{
command='stopinsert',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_stopinsert',
},
@@ -2658,19 +2659,19 @@ module.cmds = {
},
{
command='sunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='sunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='suspend',
- flags=bit.bor(TRLBAR, BANG, CMDWIN),
+ flags=bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_stop',
},
@@ -2682,19 +2683,19 @@ module.cmds = {
},
{
command='swapname',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_swapname',
},
{
command='syntax',
- flags=bit.bor(EXTRA, NOTRLCOM, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_syntax',
},
{
command='syntime',
- flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_syntime',
},
@@ -2706,19 +2707,19 @@ module.cmds = {
},
{
command='t',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_copymove',
},
{
command='tcd',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
{
command='tchdir',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
@@ -2736,7 +2737,7 @@ module.cmds = {
},
{
command='tags',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='do_tags',
},
@@ -2748,7 +2749,7 @@ module.cmds = {
},
{
command='tabclose',
- flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_TABS',
func='ex_tabclose',
},
@@ -2802,7 +2803,7 @@ module.cmds = {
},
{
command='tabonly',
- flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_TABS',
func='ex_tabonly',
},
@@ -2826,31 +2827,31 @@ module.cmds = {
},
{
command='tabs',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_tabs',
},
{
command='tcl',
- flags=bit.bor(RANGE,EXTRA,NEEDARG,CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_script_ni',
},
{
command='tcldo',
- flags=bit.bor(RANGE,DFLALL,EXTRA,NEEDARG,CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_ni',
},
{
command='tclfile',
- flags=bit.bor(RANGE,FILE1,NEEDARG,CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_ni',
},
{
command='terminal',
- flags=bit.bor(BANG, FILES, CMDWIN),
+ flags=bit.bor(BANG, FILES, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_terminal',
},
@@ -2862,7 +2863,7 @@ module.cmds = {
},
{
command='throw',
- flags=bit.bor(EXTRA, NEEDARG, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_throw',
},
@@ -2879,20 +2880,38 @@ module.cmds = {
func='ex_tag',
},
{
+ command='tlmenu',
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
+ addr_type='ADDR_OTHER',
+ func='ex_menu',
+ },
+ {
+ command='tlnoremenu',
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
+ addr_type='ADDR_OTHER',
+ func='ex_menu',
+ },
+ {
+ command='tlunmenu',
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
+ addr_type='ADDR_OTHER',
+ func='ex_menu',
+ },
+ {
command='tmenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='tmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='tmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
@@ -2904,7 +2923,7 @@ module.cmds = {
},
{
command='tnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
@@ -2928,7 +2947,7 @@ module.cmds = {
},
{
command='try',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_try',
},
@@ -2940,37 +2959,37 @@ module.cmds = {
},
{
command='tunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='tunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='undo',
- flags=bit.bor(BANG, RANGE, COUNT, ZEROR, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, COUNT, ZEROR, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_undo',
},
{
command='undojoin',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_undojoin',
},
{
command='undolist',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_undolist',
},
{
command='unabbreviate',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
@@ -2982,31 +3001,31 @@ module.cmds = {
},
{
command='unlet',
- flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unlet',
},
{
command='unlockvar',
- flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_lockvar',
},
{
command='unmap',
- flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='unmenu',
- flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='unsilent',
- flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_wrongmodifier',
},
@@ -3018,19 +3037,19 @@ module.cmds = {
},
{
command='vglobal',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, DFLALL, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, DFLALL, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_global',
},
{
command='version',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_version',
},
{
command='verbose',
- flags=bit.bor(NEEDARG, RANGE, EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(NEEDARG, RANGE, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_wrongmodifier',
},
@@ -3054,13 +3073,13 @@ module.cmds = {
},
{
command='vimgrep',
- flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE),
+ flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_vimgrep',
},
{
command='vimgrepadd',
- flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE),
+ flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_vimgrep',
},
@@ -3072,25 +3091,25 @@ module.cmds = {
},
{
command='vmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='vmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='vmenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='vnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
@@ -3102,7 +3121,7 @@ module.cmds = {
},
{
command='vnoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -3114,19 +3133,19 @@ module.cmds = {
},
{
command='vunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='vunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='write',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_write',
},
@@ -3138,13 +3157,13 @@ module.cmds = {
},
{
command='wall',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='do_wqall',
},
{
command='while',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_while',
},
@@ -3156,7 +3175,7 @@ module.cmds = {
},
{
command='wincmd',
- flags=bit.bor(NEEDARG, WORD1, RANGE, CMDWIN),
+ flags=bit.bor(NEEDARG, WORD1, RANGE, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_wincmd',
},
@@ -3168,7 +3187,7 @@ module.cmds = {
},
{
command='winpos',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_ni',
},
@@ -3198,7 +3217,7 @@ module.cmds = {
},
{
command='wshada',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_shada',
},
@@ -3210,13 +3229,13 @@ module.cmds = {
},
{
command='wviminfo',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_shada',
},
{
command='xit',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_exit',
},
@@ -3228,120 +3247,122 @@ module.cmds = {
},
{
command='xmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='xmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='xmenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='xnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='xnoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='xunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='xunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='yank',
- flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_operators',
},
{
command='z',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, FLAGS, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, FLAGS, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_z',
},
+ -- commands that don't start with a letter
{
command='!',
enum='CMD_bang',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILES, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILES, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_bang',
},
{
command='#',
enum='CMD_pound',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_print',
},
{
command='&',
enum='CMD_and',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_substitute',
},
{
command='<',
enum='CMD_lshift',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_operators',
},
{
command='=',
enum='CMD_equal',
- flags=bit.bor(RANGE, TRLBAR, DFLALL, FLAGS, CMDWIN),
+ flags=bit.bor(RANGE, TRLBAR, DFLALL, FLAGS, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_equal',
},
{
command='>',
enum='CMD_rshift',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_operators',
},
{
command='@',
enum='CMD_at',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_at',
},
{
- command='Next',
- flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR),
- addr_type='ADDR_OTHER',
- func='ex_previous',
- },
- {
command='~',
enum='CMD_tilde',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_substitute',
},
+ -- commands that start with an uppercase letter
+ {
+ command='Next',
+ flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR),
+ addr_type='ADDR_OTHER',
+ func='ex_previous',
+ },
}
return module
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 621004103e..e57dc5d13f 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -562,7 +562,7 @@ void dialog_changed(buf_T *buf, bool checkall)
if (ret == VIM_YES) {
if (buf->b_fname != NULL
- && check_overwrite(&ea, buf, buf->b_fname, (char *)buf->b_ffname, false) == OK) {
+ && check_overwrite(&ea, buf, buf->b_fname, buf->b_ffname, false) == OK) {
// didn't hit Cancel
(void)buf_write_all(buf, false);
}
@@ -578,8 +578,7 @@ void dialog_changed(buf_T *buf, bool checkall)
set_bufref(&bufref, buf2);
if (buf2->b_fname != NULL
- && check_overwrite(&ea, buf2, buf2->b_fname,
- (char *)buf2->b_ffname, false) == OK) {
+ && check_overwrite(&ea, buf2, buf2->b_fname, buf2->b_ffname, false) == OK) {
// didn't hit Cancel
(void)buf_write_all(buf2, false);
}
@@ -786,7 +785,7 @@ int buf_write_all(buf_T *buf, int forceit)
int retval;
buf_T *old_curbuf = curbuf;
- retval = (buf_write(buf, (char *)buf->b_ffname, buf->b_fname,
+ retval = (buf_write(buf, buf->b_ffname, buf->b_fname,
(linenr_T)1, buf->b_ml.ml_line_count, NULL,
false, forceit, true, false));
if (curbuf != old_curbuf) {
@@ -929,7 +928,7 @@ static int do_arglist(char *str, int what, int after, bool will_edit)
didone = false;
for (match = 0; match < ARGCOUNT; match++) {
- if (vim_regexec(&regmatch, (char_u *)alist_name(&ARGLIST[match]), (colnr_T)0)) {
+ if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]), (colnr_T)0)) {
didone = true;
xfree(ARGLIST[match].ae_fname);
memmove(ARGLIST + match, ARGLIST + match + 1,
@@ -992,7 +991,7 @@ static bool editing_arg_idx(win_T *win)
!= WARGLIST(win)[win->w_arg_idx].ae_fnum
&& (win->w_buffer->b_ffname == NULL
|| !(path_full_compare(alist_name(&WARGLIST(win)[win->w_arg_idx]),
- (char *)win->w_buffer->b_ffname, true,
+ win->w_buffer->b_ffname, true,
true) & kEqualFiles))));
}
@@ -1011,7 +1010,7 @@ void check_arg_idx(win_T *win)
&& (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
|| (win->w_buffer->b_ffname != NULL
&& (path_full_compare(alist_name(&GARGLIST[GARGCOUNT - 1]),
- (char *)win->w_buffer->b_ffname, true, true)
+ win->w_buffer->b_ffname, true, true)
& kEqualFiles)))) {
arg_had_last = true;
}
@@ -1019,8 +1018,7 @@ void check_arg_idx(win_T *win)
// We are editing the current entry in the argument list.
// Set "arg_had_last" if it's also the last one
win->w_arg_idx_invalid = false;
- if (win->w_arg_idx == WARGCOUNT(win) - 1
- && win->w_alist == &global_alist) {
+ if (win->w_arg_idx == WARGCOUNT(win) - 1 && win->w_alist == &global_alist) {
arg_had_last = true;
}
}
@@ -1138,7 +1136,7 @@ void do_argfile(exarg_T *eap, int argn)
other = true;
if (buf_hide(curbuf)) {
p = fix_fname(alist_name(&ARGLIST[argn]));
- other = otherfile((char_u *)p);
+ other = otherfile(p);
xfree(p);
}
if ((!buf_hide(curbuf) || !other)
@@ -1151,8 +1149,7 @@ void do_argfile(exarg_T *eap, int argn)
}
curwin->w_arg_idx = argn;
- if (argn == ARGCOUNT - 1
- && curwin->w_alist == &global_alist) {
+ if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist) {
arg_had_last = true;
}
@@ -1551,7 +1548,7 @@ static void alist_add_list(int count, char **files, int after, bool will_edit)
for (int i = 0; i < count; i++) {
const int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
ARGLIST[after + i].ae_fname = (char_u *)files[i];
- ARGLIST[after + i].ae_fnum = buflist_add((char_u *)files[i], flags);
+ ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
}
ALIST(curwin)->al_ga.ga_len += count;
if (old_argcount > 0 && curwin->w_arg_idx >= after) {
@@ -1641,7 +1638,7 @@ void ex_options(exarg_T *eap)
bool multi_mods = 0;
buf[0] = NUL;
- (void)add_win_cmd_modifers(buf, &multi_mods);
+ (void)add_win_cmd_modifers(buf, &cmdmod, &multi_mods);
os_setenv("OPTWIN_CMD", buf, 1);
cmd_source(SYS_OPTWIN_FILE, NULL);
@@ -2157,31 +2154,23 @@ scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx)
sctx_T script_sctx = { .sc_seq = ++last_current_SID_seq,
.sc_lnum = 0,
.sc_sid = 0 };
- FileID file_id;
scriptitem_T *si = NULL;
- bool file_id_ok = os_fileid((char *)fname, &file_id);
assert(script_items.ga_len >= 0);
- for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0;
- script_sctx.sc_sid--) {
+ for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0; script_sctx.sc_sid--) {
+ // We used to check inode here, but that doesn't work:
+ // - If a script is edited and written, it may get a different
+ // inode number, even though to the user it is the same script.
+ // - If a script is deleted and another script is written, with a
+ // different name, the inode may be re-used.
si = &SCRIPT_ITEM(script_sctx.sc_sid);
- // Compare dev/ino when possible, it catches symbolic links.
- // Also compare file names, the inode may change when the file was edited.
- bool file_id_equal = file_id_ok && si->file_id_valid
- && os_fileid_equal(&(si->file_id), &file_id);
- if (si->sn_name != NULL
- && (file_id_equal || FNAMECMP(si->sn_name, fname) == 0)) {
+ if (si->sn_name != NULL && FNAMECMP(si->sn_name, fname) == 0) {
+ // Found it!
break;
}
}
if (script_sctx.sc_sid == 0) {
si = new_script_item((char *)vim_strsave(fname), &script_sctx.sc_sid);
- if (file_id_ok) {
- si->file_id_valid = true;
- si->file_id = file_id;
- } else {
- si->file_id_valid = false;
- }
}
if (ret_sctx != NULL) {
*ret_sctx = script_sctx;
@@ -2265,7 +2254,7 @@ char_u *get_scriptname(LastSet last_set, bool *should_free)
}
*should_free = true;
- return home_replace_save(NULL, (char_u *)sname);
+ return (char_u *)home_replace_save(NULL, sname);
}
}
}
@@ -2494,8 +2483,7 @@ void script_line_start(void)
if (si->sn_prof_on && sourcing_lnum >= 1) {
// Grow the array before starting the timer, so that the time spent
// here isn't counted.
- (void)ga_grow(&si->sn_prl_ga,
- (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
+ (void)ga_grow(&si->sn_prl_ga, sourcing_lnum - si->sn_prl_ga.ga_len);
si->sn_prl_idx = sourcing_lnum - 1;
while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
&& si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) {
diff --git a/src/nvim/ex_cmds2.h b/src/nvim/ex_cmds2.h
index 74e52dfb4b..c463bfa5ab 100644
--- a/src/nvim/ex_cmds2.h
+++ b/src/nvim/ex_cmds2.h
@@ -15,12 +15,8 @@
#define CCGD_ALLBUF 8 // may write all buffers
#define CCGD_EXCMD 16 // may suggest using !
-/// Also store the dev/ino, so that we don't have to stat() each
-/// script when going through the list.
typedef struct scriptitem_S {
char_u *sn_name;
- bool file_id_valid;
- FileID file_id;
bool sn_prof_on; ///< true when script is/was profiled
bool sn_pr_force; ///< forceit: profile functions in this script
proftime_T sn_pr_child; ///< time set when going into first child
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 6798f7693b..052926fa1f 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -57,11 +57,12 @@
#define EX_BUFUNL 0x10000 // accepts unlisted buffer too
#define EX_ARGOPT 0x20000 // allow "++opt=val" argument
#define EX_SBOXOK 0x40000 // allowed in the sandbox
-#define EX_CMDWIN 0x80000 // allowed in cmdline window; when missing
- // disallows editing another buffer when
- // current buffer is locked
+#define EX_CMDWIN 0x80000 // allowed in cmdline window
#define EX_MODIFY 0x100000 // forbidden in non-'modifiable' buffer
#define EX_FLAGS 0x200000 // allow flags after count in argument
+#define EX_LOCK_OK 0x1000000 // command can be executed when textlock is
+ // set; when missing disallows editing another
+ // buffer when current buffer is locked
#define EX_KEEPSCRIPT 0x4000000 // keep sctx of where command was invoked
#define EX_PREVIEW 0x8000000 // allow incremental command preview
#define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index fee98a18dc..c4d2821e79 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -62,6 +62,7 @@
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/popupmnu.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
@@ -226,6 +227,8 @@ void do_exmode(void)
emsg(_(e_emptybuf));
} else {
if (ex_pressedreturn) {
+ // Make sure the message overwrites the right line and isn't throttled.
+ msg_scroll_flush();
// go up one line, to overwrite the ":<CR>" line, so the
// output doesn't contain empty lines.
msg_row = prev_msg_row;
@@ -907,6 +910,15 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
msg_list = saved_msg_list;
+ // Cleanup if "cs_emsg_silent_list" remains.
+ if (cstack.cs_emsg_silent_list != NULL) {
+ eslist_T *elem, *temp;
+ for (elem = cstack.cs_emsg_silent_list; elem != NULL; elem = temp) {
+ temp = elem->next;
+ xfree(elem);
+ }
+ }
+
/*
* If there was too much output to fit on the command line, ask the user to
* hit return before redrawing the screen. With the ":global" command we do
@@ -1432,10 +1444,13 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
eap->getline = NULL;
eap->cookie = NULL;
+ const bool save_ex_pressedreturn = ex_pressedreturn;
// Parse command modifiers
if (parse_command_modifiers(eap, errormsg, &cmdinfo->cmdmod, false) == FAIL) {
+ ex_pressedreturn = save_ex_pressedreturn;
goto err;
}
+ ex_pressedreturn = save_ex_pressedreturn;
after_modifier = eap->cmd;
// Save location after command modifiers
@@ -1575,9 +1590,15 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview)
&& !(curbuf->terminal && eap->cmdidx == CMD_put)) {
ERROR(_(e_modifiable));
}
- if (text_locked() && !(eap->argt & EX_CMDWIN)
- && !IS_USER_CMDIDX(eap->cmdidx)) {
- ERROR(_(get_text_locked_msg()));
+ if (!IS_USER_CMDIDX(eap->cmdidx)) {
+ if (cmdwin_type != 0 && !(eap->argt & EX_CMDWIN)) {
+ // Command not allowed in the command line window
+ ERROR(_(e_cmdwin));
+ }
+ if (text_locked() && !(eap->argt & EX_LOCK_OK)) {
+ // Command not allowed when text is locked
+ ERROR(_(get_text_locked_msg()));
+ }
}
// Disallow editing another buffer when "curbuf->b_ro_locked" is set.
// Do allow ":checktime" (it is postponed).
@@ -1620,21 +1641,21 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview)
if (eap->cmdidx == CMD_bdelete || eap->cmdidx == CMD_bwipeout
|| eap->cmdidx == CMD_bunload) {
- p = (char *)skiptowhite_esc((char_u *)eap->arg);
+ p = skiptowhite_esc(eap->arg);
} else {
p = eap->arg + STRLEN(eap->arg);
while (p > eap->arg && ascii_iswhite(p[-1])) {
p--;
}
}
- eap->line2 = buflist_findpat((char_u *)eap->arg, (char_u *)p, (eap->argt & EX_BUFUNL) != 0,
+ eap->line2 = buflist_findpat(eap->arg, p, (eap->argt & EX_BUFUNL) != 0,
false, false);
eap->addr_count = 1;
eap->arg = skipwhite(p);
} else {
// If argument positions are specified, just use the first argument
- eap->line2 = buflist_findpat((char_u *)eap->args[0],
- (char_u *)(eap->args[0] + eap->arglens[0]),
+ eap->line2 = buflist_findpat(eap->args[0],
+ eap->args[0] + eap->arglens[0],
(eap->argt & EX_BUFUNL) != 0, false, false);
eap->addr_count = 1;
// Shift each argument by 1
@@ -1952,11 +1973,17 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
goto doend;
}
- if (text_locked() && !(ea.argt & EX_CMDWIN)
- && !IS_USER_CMDIDX(ea.cmdidx)) {
- // Command not allowed when editing the command line.
- errormsg = _(get_text_locked_msg());
- goto doend;
+ if (!IS_USER_CMDIDX(ea.cmdidx)) {
+ if (cmdwin_type != 0 && !(ea.argt & EX_CMDWIN)) {
+ // Command not allowed in the command line window
+ errormsg = _(e_cmdwin);
+ goto doend;
+ }
+ if (text_locked() && !(ea.argt & EX_LOCK_OK)) {
+ // Command not allowed when text is locked
+ errormsg = _(get_text_locked_msg());
+ goto doend;
+ }
}
// Disallow editing another buffer when "curbuf->b_ro_locked" is set.
@@ -2280,14 +2307,14 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
*/
if (ea.cmdidx == CMD_bdelete || ea.cmdidx == CMD_bwipeout
|| ea.cmdidx == CMD_bunload) {
- p = (char *)skiptowhite_esc((char_u *)ea.arg);
+ p = skiptowhite_esc(ea.arg);
} else {
p = ea.arg + STRLEN(ea.arg);
while (p > ea.arg && ascii_iswhite(p[-1])) {
p--;
}
}
- ea.line2 = buflist_findpat((char_u *)ea.arg, (char_u *)p, (ea.argt & EX_BUFUNL) != 0,
+ ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & EX_BUFUNL) != 0,
false, false);
if (ea.line2 < 0) { // failed
goto doend;
@@ -2340,7 +2367,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
do_return(&ea, TRUE, FALSE, NULL);
}
}
- need_rethrow = check_cstack = FALSE;
+ need_rethrow = check_cstack = false;
doend:
// can happen with zero line number
@@ -2395,7 +2422,8 @@ char *ex_errmsg(const char *const msg, const char *const arg)
/// - Set ex_pressedreturn for an empty command line.
///
/// @param skip_only if false, undo_cmdmod() must be called later to free
-/// any cmod_filter_pat and cmod_filter_regmatch.regprog.
+/// any cmod_filter_pat and cmod_filter_regmatch.regprog,
+/// and ex_pressedreturn may be set.
/// @param[out] errormsg potential error message.
///
/// Call apply_cmdmod() to get the side effects of the modifiers:
@@ -2409,7 +2437,7 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool
{
char *p;
- memset(cmod, 0, sizeof(*cmod));
+ CLEAR_POINTER(cmod);
// Repeat until no more command modifiers are found.
for (;;) {
@@ -2622,7 +2650,7 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool
}
if (ascii_isdigit(*eap->cmd)) {
// zero means not set, one is verbose == 0, etc.
- cmod->cmod_verbose = atoi((char *)eap->cmd) + 1;
+ cmod->cmod_verbose = atoi(eap->cmd) + 1;
} else {
cmod->cmod_verbose = 2; // default: verbose == 1
}
@@ -2820,16 +2848,16 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent)
eap->cmd++;
if (!eap->skip) {
- pos_T *fp = getmark('<', false);
- if (check_mark(fp) == FAIL) {
+ fmark_T *fm = mark_get_visual(curbuf, '<');
+ if (!mark_check(fm)) {
goto theend;
}
- eap->line1 = fp->lnum;
- fp = getmark('>', false);
- if (check_mark(fp) == FAIL) {
+ eap->line1 = fm->mark.lnum;
+ fm = mark_get_visual(curbuf, '>');
+ if (!mark_check(fm)) {
goto theend;
}
- eap->line2 = fp->lnum;
+ eap->line2 = fm->mark.lnum;
eap->addr_count++;
}
}
@@ -2903,9 +2931,16 @@ int checkforcmd(char **pp, char *cmd, int len)
/// invisible otherwise.
static void append_command(char *cmd)
{
+ size_t len = STRLEN(IObuff);
char *s = cmd;
char *d;
+ if (len > IOSIZE - 100) {
+ // Not enough space, truncate and put in "...".
+ d = (char *)IObuff + IOSIZE - 100;
+ d -= utf_head_off(IObuff, (const char_u *)d);
+ STRCPY(d, "...");
+ }
STRCAT(IObuff, ": ");
d = (char *)IObuff + STRLEN(IObuff);
while (*s != NUL && (char_u *)d - IObuff + 5 < IOSIZE) {
@@ -3009,6 +3044,8 @@ char *find_ex_command(exarg_T *eap, int *full)
if (ASCII_ISLOWER(c2)) {
eap->cmdidx += cmdidxs2[CHAR_ORD_LOW(c1)][CHAR_ORD_LOW(c2)];
}
+ } else if (ASCII_ISUPPER(eap->cmd[0])) {
+ eap->cmdidx = CMD_Next;
} else {
eap->cmdidx = CMD_bang;
}
@@ -4059,6 +4096,9 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_cmenu:
case CMD_cnoremenu:
case CMD_cunmenu:
+ case CMD_tlmenu:
+ case CMD_tlnoremenu:
+ case CMD_tlunmenu:
case CMD_tmenu:
case CMD_tunmenu:
case CMD_popup:
@@ -4230,7 +4270,6 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
linenr_T n;
char *cmd;
pos_T pos;
- pos_T *fp;
linenr_T lnum;
buf_T *buf;
@@ -4340,17 +4379,18 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
} else {
// Only accept a mark in another file when it is
// used by itself: ":'M".
- fp = getmark(*cmd, to_other_file && cmd[1] == NUL);
- ++cmd;
- if (fp == (pos_T *)-1) {
+ MarkGet flag = to_other_file && cmd[1] == NUL ? kMarkAll : kMarkBufLocal;
+ fmark_T *fm = mark_get(curbuf, curwin, NULL, flag, *cmd);
+ cmd++;
+ if (fm != NULL && fm->fnum != curbuf->handle) {
// Jumped to another file.
lnum = curwin->w_cursor.lnum;
} else {
- if (check_mark(fp) == FAIL) {
+ if (!mark_check(fm)) {
cmd = NULL;
goto error;
}
- lnum = fp->lnum;
+ lnum = fm->mark.lnum;
}
}
break;
@@ -4594,8 +4634,9 @@ char *invalid_range(exarg_T *eap)
}
break;
case ADDR_BUFFERS:
- if (eap->line1 < firstbuf->b_fnum
- || eap->line2 > lastbuf->b_fnum) {
+ // Only a boundary check, not whether the buffers actually
+ // exist.
+ if (eap->line1 < 1 || eap->line2 > get_highest_fnum()) {
return _(e_invrange);
}
break;
@@ -5313,7 +5354,7 @@ static void ex_bunload(exarg_T *eap)
: eap->cmdidx == CMD_bwipeout
? DOBUF_WIPE
: DOBUF_UNLOAD,
- (char_u *)eap->arg, eap->addr_count, (int)eap->line1, (int)eap->line2,
+ eap->arg, eap->addr_count, (int)eap->line1, (int)eap->line2,
eap->forceit);
}
@@ -6450,20 +6491,7 @@ static size_t uc_check_code(char *code, size_t len, char *buf, ucmd_T *cmd, exar
}
case ct_MODS:
- result = quote ? 2 : 0;
- if (buf != NULL) {
- if (quote) {
- *buf++ = '"';
- }
- *buf = '\0';
- }
-
- result += uc_mods(buf);
-
- if (quote && buf != NULL) {
- buf += result - 2;
- *buf = '"';
- }
+ result = uc_mods(buf, &cmdmod, quote);
break;
case ct_REGISTER:
@@ -6503,43 +6531,45 @@ static size_t uc_check_code(char *code, size_t len, char *buf, ucmd_T *cmd, exar
return result;
}
-/// Add modifiers from "cmdmod.cmod_split" to "buf". Set "multi_mods" when one
+/// Add modifiers from "cmod->cmod_split" to "buf". Set "multi_mods" when one
/// was added.
///
/// @return the number of bytes added
-size_t add_win_cmd_modifers(char *buf, bool *multi_mods)
+size_t add_win_cmd_modifers(char *buf, const cmdmod_T *cmod, bool *multi_mods)
{
size_t result = 0;
// :aboveleft and :leftabove
- if (cmdmod.cmod_split & WSP_ABOVE) {
+ if (cmod->cmod_split & WSP_ABOVE) {
result += add_cmd_modifier(buf, "aboveleft", multi_mods);
}
// :belowright and :rightbelow
- if (cmdmod.cmod_split & WSP_BELOW) {
+ if (cmod->cmod_split & WSP_BELOW) {
result += add_cmd_modifier(buf, "belowright", multi_mods);
}
// :botright
- if (cmdmod.cmod_split & WSP_BOT) {
+ if (cmod->cmod_split & WSP_BOT) {
result += add_cmd_modifier(buf, "botright", multi_mods);
}
// :tab
- if (cmdmod.cmod_tab > 0) {
+ if (cmod->cmod_tab > 0) {
result += add_cmd_modifier(buf, "tab", multi_mods);
}
// :topleft
- if (cmdmod.cmod_split & WSP_TOP) {
+ if (cmod->cmod_split & WSP_TOP) {
result += add_cmd_modifier(buf, "topleft", multi_mods);
}
// :vertical
- if (cmdmod.cmod_split & WSP_VERT) {
+ if (cmod->cmod_split & WSP_VERT) {
result += add_cmd_modifier(buf, "vertical", multi_mods);
}
return result;
}
-size_t uc_mods(char *buf)
+/// Generate text for the "cmod" command modifiers.
+/// If "buf" is NULL just return the length.
+size_t uc_mods(char *buf, const cmdmod_T *cmod, bool quote)
{
size_t result = 0;
bool multi_mods = false;
@@ -6557,32 +6587,51 @@ size_t uc_mods(char *buf)
{ CMOD_KEEPMARKS, "keepmarks" },
{ CMOD_KEEPPATTERNS, "keeppatterns" },
{ CMOD_LOCKMARKS, "lockmarks" },
- { CMOD_NOSWAPFILE, "noswapfile" }
+ { CMOD_NOSWAPFILE, "noswapfile" },
+ { CMOD_UNSILENT, "unsilent" },
+ { CMOD_NOAUTOCMD, "noautocmd" },
+ { CMOD_SANDBOX, "sandbox" },
};
+
+ result = quote ? 2 : 0;
+ if (buf != NULL) {
+ if (quote) {
+ *buf++ = '"';
+ }
+ *buf = '\0';
+ }
+
// the modifiers that are simple flags
for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) {
- if (cmdmod.cmod_flags & mod_entries[i].flag) {
+ if (cmod->cmod_flags & mod_entries[i].flag) {
result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
}
}
- // TODO(vim): How to support :noautocmd?
- // TODO(vim): How to support :sandbox?
-
// :silent
- if (msg_silent > 0) {
- result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent", &multi_mods);
+ if (cmod->cmod_flags & CMOD_SILENT) {
+ result += add_cmd_modifier(buf,
+ (cmod->cmod_flags & CMOD_ERRSILENT) ? "silent!" : "silent",
+ &multi_mods);
}
-
- // TODO(vim): How to support :unsilent?
-
// :verbose
- if (p_verbose > 0) {
- result += add_cmd_modifier(buf, "verbose", &multi_mods);
+ if (cmod->cmod_verbose > 0) {
+ int verbose_value = cmod->cmod_verbose - 1;
+ if (verbose_value == 1) {
+ result += add_cmd_modifier(buf, "verbose", &multi_mods);
+ } else {
+ char verbose_buf[NUMBUFLEN];
+ snprintf(verbose_buf, NUMBUFLEN, "%dverbose", verbose_value);
+ result += add_cmd_modifier(buf, verbose_buf, &multi_mods);
+ }
}
- // flags from cmdmod.cmod_split
- result += add_win_cmd_modifers(buf, &multi_mods);
+ // flags from cmod->cmod_split
+ result += add_win_cmd_modifers(buf, cmod, &multi_mods);
+ if (quote && buf != NULL) {
+ buf += result - 2;
+ *buf = '"';
+ }
return result;
}
@@ -7502,7 +7551,7 @@ void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_l
/* May set buffer name of a buffer previously used for the
* argument list, so that it's re-used by alist_add. */
if (fnum_list != NULL && i < fnum_len) {
- buf_set_name(fnum_list[i], (char_u *)files[i]);
+ buf_set_name(fnum_list[i], files[i]);
}
alist_add(al, files[i], use_curbuf ? 2 : 1);
@@ -7532,7 +7581,7 @@ void alist_add(alist_T *al, char *fname, int set_fnum)
AARGLIST(al)[al->al_ga.ga_len].ae_fname = (char_u *)fname;
if (set_fnum > 0) {
AARGLIST(al)[al->al_ga.ga_len].ae_fnum =
- buflist_add((char_u *)fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0));
+ buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0));
}
++al->al_ga.ga_len;
}
@@ -7622,7 +7671,7 @@ void ex_splitview(exarg_T *eap)
if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) {
fname = (char *)find_file_in_path((char_u *)eap->arg, STRLEN(eap->arg),
- FNAME_MESS, true, curbuf->b_ffname);
+ FNAME_MESS, true, (char_u *)curbuf->b_ffname);
if (fname == NULL) {
goto theend;
}
@@ -7824,14 +7873,14 @@ static void ex_find(exarg_T *eap)
linenr_T count;
fname = (char *)find_file_in_path((char_u *)eap->arg, STRLEN(eap->arg),
- FNAME_MESS, true, curbuf->b_ffname);
+ FNAME_MESS, true, (char_u *)curbuf->b_ffname);
if (eap->addr_count > 0) {
// Repeat finding the file "count" times. This matters when it
// appears several times in the path.
count = eap->line2;
while (fname != NULL && --count > 0) {
xfree(fname);
- fname = (char *)find_file_in_path(NULL, 0, FNAME_MESS, false, curbuf->b_ffname);
+ fname = (char *)find_file_in_path(NULL, 0, FNAME_MESS, false, (char_u *)curbuf->b_ffname);
}
}
@@ -7880,9 +7929,11 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
need_wait_return = false;
msg_scroll = 0;
redraw_all_later(NOT_VALID);
+ pending_exmode_active = true;
normal_enter(false, true);
+ pending_exmode_active = false;
RedrawingDisabled = rd;
no_wait_return = nwr;
msg_scroll = ms;
@@ -7984,6 +8035,11 @@ static void ex_nogui(exarg_T *eap)
eap->errmsg = N_("E25: Nvim does not have a built-in GUI");
}
+static void ex_popup(exarg_T *eap)
+{
+ pum_make_popup(eap->arg, eap->forceit);
+}
+
static void ex_swapname(exarg_T *eap)
{
if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL) {
@@ -8077,11 +8133,11 @@ static void ex_read(exarg_T *eap)
if (check_fname() == FAIL) { // check for no file name
return;
}
- i = readfile((char *)curbuf->b_ffname, curbuf->b_fname,
+ i = readfile(curbuf->b_ffname, curbuf->b_fname,
eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0, false);
} else {
if (vim_strchr(p_cpo, CPO_ALTREAD) != NULL) {
- (void)setaltfname((char_u *)eap->arg, (char_u *)eap->arg, (linenr_T)1);
+ (void)setaltfname(eap->arg, eap->arg, (linenr_T)1);
}
i = readfile(eap->arg, NULL,
eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0, false);
@@ -8129,10 +8185,10 @@ static char *get_prevdir(CdScope scope)
{
switch (scope) {
case kCdScopeTabpage:
- return (char *)curtab->tp_prevdir;
+ return curtab->tp_prevdir;
break;
case kCdScopeWindow:
- return (char *)curwin->w_prevdir;
+ return curwin->w_prevdir;
break;
default:
return prev_dir;
@@ -8170,10 +8226,10 @@ static void post_chdir(CdScope scope, bool trigger_dirchanged)
XFREE_CLEAR(globaldir);
break;
case kCdScopeTabpage:
- curtab->tp_localdir = (char_u *)xstrdup(cwd);
+ curtab->tp_localdir = xstrdup(cwd);
break;
case kCdScopeWindow:
- curwin->w_localdir = (char_u *)xstrdup(cwd);
+ curwin->w_localdir = xstrdup(cwd);
break;
case kCdScopeInvalid:
abort();
@@ -8239,10 +8295,10 @@ bool changedir_func(char *new_dir, CdScope scope)
char **pp;
switch (scope) {
case kCdScopeTabpage:
- pp = (char **)&curtab->tp_prevdir;
+ pp = &curtab->tp_prevdir;
break;
case kCdScopeWindow:
- pp = (char **)&curwin->w_prevdir;
+ pp = &curwin->w_prevdir;
break;
default:
pp = &prev_dir;
@@ -8370,10 +8426,10 @@ static void ex_winsize(exarg_T *eap)
semsg(_(e_invarg2), arg);
return;
}
- int w = getdigits_int((char_u **)&arg, false, 10);
+ int w = getdigits_int(&arg, false, 10);
arg = skipwhite(arg);
char *p = arg;
- int h = getdigits_int((char_u **)&arg, false, 10);
+ int h = getdigits_int(&arg, false, 10);
if (*p != NUL && *arg == NUL) {
screen_resize(w, h);
} else {
@@ -8549,7 +8605,7 @@ static void ex_join(exarg_T *eap)
}
++eap->line2;
}
- do_join((size_t)(eap->line2 - eap->line1 + 1), !eap->forceit, true, true, true);
+ do_join((size_t)((ssize_t)eap->line2 - eap->line1 + 1), !eap->forceit, true, true, true);
beginline(BL_WHITE | BL_FIX);
ex_may_print(eap);
}
@@ -9468,7 +9524,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
if (*s == '<') { // "#<99" uses v:oldfiles.
s++;
}
- i = getdigits_int((char_u **)&s, false, 0);
+ i = getdigits_int(&s, false, 0);
if ((char_u *)s == src + 2 && src[1] == '-') {
// just a minus sign, don't skip over it
s--;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 01f16f3383..4c26cfe500 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -688,12 +688,12 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool
/// @param init_ccline clear ccline first
static uint8_t *command_line_enter(int firstc, long count, int indent, bool init_ccline)
{
- bool cmdheight0 = p_ch < 1 && !ui_has(kUIMessages) && vpeekc() == NUL;
+ bool cmdheight0 = p_ch < 1 && !ui_has(kUIMessages);
if (cmdheight0) {
// If cmdheight is 0, cmdheight must be set to 1 when we enter command line.
set_option_value("ch", 1L, NULL, 0);
- redraw_statuslines();
+ update_screen(VALID); // redraw the screen NOW
}
// can be invoked recursively, identify each level
@@ -985,6 +985,9 @@ theend:
if (cmdheight0) {
// Restore cmdheight
set_option_value("ch", 0L, NULL, 0);
+
+ // Redraw is needed for command line completion
+ redraw_all_later(CLEAR);
}
return p;
@@ -2643,7 +2646,7 @@ char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_
/// @param[in] highlight_callback Callback used for highlighting user input.
///
/// @return [allocated] Command line or NULL.
-char *getcmdline_prompt(const char firstc, const char *const prompt, const int attr,
+char *getcmdline_prompt(const int firstc, const char *const prompt, const int attr,
const int xp_context, const char *const xp_arg,
const Callback highlight_callback)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
@@ -2693,14 +2696,12 @@ char_u *get_cmdprompt(void)
return ccline.cmdprompt;
}
-/*
- * Return TRUE when the text must not be changed and we can't switch to
- * another window or buffer. Used when editing the command line etc.
- */
-int text_locked(void)
+/// Return true when the text must not be changed and we can't switch to
+/// another window or buffer. True when editing the command line etc.
+bool text_locked(void)
{
if (cmdwin_type != 0) {
- return TRUE;
+ return true;
}
return textlock != 0;
}
@@ -2719,10 +2720,21 @@ char *get_text_locked_msg(void)
if (cmdwin_type != 0) {
return e_cmdwin;
} else {
- return e_secure;
+ return e_textlock;
}
}
+/// Check for text, window or buffer locked.
+/// Give an error message and return true if something is locked.
+bool text_or_buf_locked(void)
+{
+ if (text_locked()) {
+ text_locked_msg();
+ return true;
+ }
+ return curbuf_locked();
+}
+
/// Check if "curbuf->b_ro_locked" or "allbuf_lock" is set and
/// return true when it is and give an error message.
bool curbuf_locked(void)
@@ -3210,7 +3222,7 @@ static void draw_cmdline(int start, int len)
int u8cc[MAX_MCO];
int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
mb_l = utfc_ptr2len_len(p, start + len - i);
- if (arabic_char(u8c)) {
+ if (ARABIC_CHAR(u8c)) {
do_arabicshape = true;
break;
}
@@ -3246,7 +3258,7 @@ static void draw_cmdline(int start, int len)
int u8cc[MAX_MCO];
int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
mb_l = utfc_ptr2len_len(p, start + len - i);
- if (arabic_char(u8c)) {
+ if (ARABIC_CHAR(u8c)) {
int pc;
int pc1 = 0;
int nc = 0;
@@ -4320,6 +4332,7 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o
{
int i;
char_u *p;
+ const int vse_what = xp->xp_context == EXPAND_BUFFERS ? VSE_BUFFER : VSE_NONE;
/*
* May change home directory back to "~"
@@ -4351,10 +4364,10 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o
#endif
}
#ifdef BACKSLASH_IN_FILENAME
- p = (char_u *)vim_strsave_fnameescape((const char *)files[i], false);
+ p = (char_u *)vim_strsave_fnameescape((const char *)files[i], vse_what);
#else
p = (char_u *)vim_strsave_fnameescape((const char *)files[i],
- xp->xp_shell);
+ xp->xp_shell ? VSE_SHELL : vse_what);
#endif
xfree(files[i]);
files[i] = p;
@@ -4386,25 +4399,30 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o
}
}
-/// Escape special characters in a file name for use as a command argument
+/// Escape special characters in "fname", depending on "what":
///
/// @param[in] fname File name to escape.
-/// @param[in] shell What to escape for: if false, escapes for VimL command,
-/// if true then it escapes for a shell command.
+/// @param[in] what What to escape for:
+/// - VSE_NONE: for when used as a file name argument after a Vim command.
+/// - VSE_SHELL: for a shell command.
+/// - VSE_BUFFER: for the ":buffer" command.
///
/// @return [allocated] escaped file name.
-char *vim_strsave_fnameescape(const char *const fname, const bool shell FUNC_ATTR_UNUSED)
+char *vim_strsave_fnameescape(const char *const fname, const int what)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
#ifdef BACKSLASH_IN_FILENAME
# define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<"
+# define BUFFER_ESC_CHARS ((char_u *)" \t\n*?[`%#'\"|!<")
char_u buf[sizeof(PATH_ESC_CHARS)];
int j = 0;
- // Don't escape '[', '{' and '!' if they are in 'isfname'.
- for (const char *s = PATH_ESC_CHARS; *s != NUL; s++) {
- if ((*s != '[' && *s != '{' && *s != '!') || !vim_isfilec(*s)) {
- buf[j++] = *s;
+ // Don't escape '[', '{' and '!' if they are in 'isfname' and for the
+ // ":buffer" command.
+ for (const char *p = what == VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS;
+ *p != NUL; p++) {
+ if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec(*p)) {
+ buf[j++] = *p;
}
}
buf[j] = NUL;
@@ -4413,9 +4431,12 @@ char *vim_strsave_fnameescape(const char *const fname, const bool shell FUNC_ATT
#else
# define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<")
# define SHELL_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<>();&")
+# define BUFFER_ESC_CHARS ((char_u *)" \t\n*?[`$\\%#'\"|!<")
char *p =
- (char *)vim_strsave_escaped((const char_u *)fname, (shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS));
- if (shell && csh_like_shell()) {
+ (char *)vim_strsave_escaped((const char_u *)fname,
+ what == VSE_SHELL ? SHELL_ESC_CHARS
+ : what == VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS);
+ if (what == VSE_SHELL && csh_like_shell()) {
// For csh and similar shells need to put two backslashes before '!'.
// One is taken by Vim, one by the shell.
char *s = (char *)vim_strsave_escaped((const char_u *)p,
@@ -4452,14 +4473,13 @@ static void escape_fname(char_u **pp)
*/
void tilde_replace(char_u *orig_pat, int num_files, char_u **files)
{
- int i;
- char_u *p;
+ char *p;
if (orig_pat[0] == '~' && vim_ispathsep(orig_pat[1])) {
- for (i = 0; i < num_files; ++i) {
- p = home_replace_save(NULL, files[i]);
+ for (int i = 0; i < num_files; i++) {
+ p = home_replace_save(NULL, (char *)files[i]);
xfree(files[i]);
- files[i] = p;
+ files[i] = (char_u *)p;
}
}
}
@@ -5161,10 +5181,10 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u **
return OK;
}
if (xp->xp_context == EXPAND_BUFFERS) {
- return ExpandBufnames(pat, num_file, file, options);
+ return ExpandBufnames((char *)pat, num_file, (char ***)file, options);
}
if (xp->xp_context == EXPAND_DIFF_BUFFERS) {
- return ExpandBufnames(pat, num_file, file, options | BUF_DIFF_FILTER);
+ return ExpandBufnames((char *)pat, num_file, (char ***)file, options | BUF_DIFF_FILTER);
}
if (xp->xp_context == EXPAND_TAGS
|| xp->xp_context == EXPAND_TAGS_LISTFILES) {
@@ -5317,8 +5337,8 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha
if (*str == NUL) { // skip empty strings
continue;
}
- if (vim_regexec(regmatch, str, (colnr_T)0)) {
- ++count;
+ if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) {
+ count++;
}
}
if (count == 0) {
@@ -5338,7 +5358,7 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha
if (*str == NUL) { // Skip empty strings.
continue;
}
- if (vim_regexec(regmatch, str, (colnr_T)0)) {
+ if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) {
if (escaped) {
str = vim_strsave_escaped(str, (char_u *)" \t\\.");
} else {
@@ -5570,7 +5590,7 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file,
*e = NUL;
const bool skip = xp->xp_pattern[0]
- && vim_regexec(regmatch, s, (colnr_T)0) == 0;
+ && vim_regexec(regmatch, (char *)s, (colnr_T)0) == 0;
*e = keep;
if (!skip) {
GA_APPEND(char_u *, &ga, vim_strnsave(s, (size_t)(e - s)));
@@ -5811,7 +5831,7 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options)
// Loop over all entries in {path}.
while (*path != NUL) {
// Copy one item of the path to buf[] and concatenate the file name.
- copy_option_part(&path, buf, MAXPATHL, ",");
+ copy_option_part((char **)&path, (char *)buf, MAXPATHL, ",");
if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL) {
add_pathsep((char *)buf);
STRCAT(buf, file); // NOLINT
@@ -6290,7 +6310,7 @@ static int calc_hist_idx(int histype, int num)
wrapped = TRUE;
}
}
- if (hist[i].hisnum == num && hist[i].hisstr != NULL) {
+ if (i >= 0 && hist[i].hisnum == num && hist[i].hisstr != NULL) {
return i;
}
} else if (-num <= hislen) {
@@ -6367,7 +6387,7 @@ int del_history_entry(int histype, char_u *str)
if (hisptr->hisstr == NULL) {
break;
}
- if (vim_regexec(&regmatch, hisptr->hisstr, (colnr_T)0)) {
+ if (vim_regexec(&regmatch, (char *)hisptr->hisstr, (colnr_T)0)) {
found = true;
hist_free_entry(hisptr);
} else {
@@ -6535,7 +6555,7 @@ void ex_history(exarg_T *eap)
snprintf((char *)IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ',
hist[i].hisnum);
if (vim_strsize((char *)hist[i].hisstr) > Columns - 10) {
- trunc_string(hist[i].hisstr, IObuff + STRLEN(IObuff),
+ trunc_string((char *)hist[i].hisstr, (char *)IObuff + STRLEN(IObuff),
Columns - 10, IOSIZE - (int)STRLEN(IObuff));
} else {
STRCAT(IObuff, hist[i].hisstr);
@@ -6598,9 +6618,9 @@ static int open_cmdwin(void)
bool save_exmode = exmode_active;
int save_cmdmsg_rl = cmdmsg_rl;
+ // Can't do this when text or buffer is locked.
// Can't do this recursively. Can't do it when typing a password.
- if (cmdwin_type != 0
- || cmdline_star > 0) {
+ if (text_or_buf_locked() || cmdwin_type != 0 || cmdline_star > 0) {
beep_flush();
return K_IGNORE;
}
diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h
index 5da9febe71..1cc6faf87c 100644
--- a/src/nvim/ex_getln.h
+++ b/src/nvim/ex_getln.h
@@ -34,6 +34,11 @@
#define WILD_BUFLASTUSED 0x1000
#define BUF_DIFF_FILTER 0x2000
+// flags used by vim_strsave_fnameescape()
+#define VSE_NONE 0
+#define VSE_SHELL 1 ///< escape for a shell command
+#define VSE_BUFFER 2 ///< escape for a ":buffer" command
+
/// Present history tables
typedef enum {
HIST_DEFAULT = -2, ///< Default (current) history.
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index 08c232ea69..6ca6da9cd0 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -187,7 +187,7 @@ static int ses_do_win(win_T *wp)
}
if (wp->w_buffer->b_fname == NULL
// When 'buftype' is "nofile" can't restore the window contents.
- || (!wp->w_buffer->terminal && bt_nofile(wp->w_buffer))) {
+ || (!wp->w_buffer->terminal && bt_nofilename(wp->w_buffer))) {
return ssop_flags & SSOP_BLANK;
}
if (bt_help(wp->w_buffer)) {
@@ -248,9 +248,9 @@ static char *ses_get_fname(buf_T *buf, unsigned *flagp)
&& (ssop_flags & (SSOP_CURDIR | SSOP_SESDIR))
&& !p_acd
&& !did_lcd) {
- return (char *)buf->b_sfname;
+ return buf->b_sfname;
}
- return (char *)buf->b_ffname;
+ return buf->b_ffname;
}
/// Write a buffer name to the session file.
@@ -275,7 +275,7 @@ static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, bool add_eol)
static char *ses_escape_fname(char *name, unsigned *flagp)
{
char *p;
- char *sname = (char *)home_replace_save(NULL, (char_u *)name);
+ char *sname = home_replace_save(NULL, name);
// Always SSOP_SLASH: change all backslashes to forward slashes.
for (p = sname; *p != NUL; MB_PTR_ADV(p)) {
@@ -285,7 +285,7 @@ static char *ses_escape_fname(char *name, unsigned *flagp)
}
// Escape special characters.
- p = vim_strsave_fnameescape(sname, false);
+ p = vim_strsave_fnameescape(sname, VSE_NONE);
xfree(sname);
return p;
}
@@ -363,7 +363,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
return FAIL;
}
} else if (wp->w_buffer->b_ffname != NULL
- && (!bt_nofile(wp->w_buffer) || wp->w_buffer->terminal)) {
+ && (!bt_nofilename(wp->w_buffer) || wp->w_buffer->terminal)) {
// Load the file.
// Editing a file in this buffer: use ":edit file".
@@ -517,7 +517,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
if (wp->w_localdir != NULL
&& (flagp != &vop_flags || (*flagp & SSOP_CURDIR))) {
if (fputs("lcd ", fd) < 0
- || ses_put_fname(fd, wp->w_localdir, flagp) == FAIL
+ || ses_put_fname(fd, (char_u *)wp->w_localdir, flagp) == FAIL
|| fprintf(fd, "\n") < 0) {
return FAIL;
}
@@ -542,7 +542,7 @@ static int makeopens(FILE *fd, char_u *dirnow)
int nr;
int restore_size = true;
win_T *wp;
- char_u *sname;
+ char *sname;
win_T *edited_win = NULL;
int tabnr;
win_T *tab_firstwin;
@@ -575,8 +575,8 @@ static int makeopens(FILE *fd, char_u *dirnow)
if (ssop_flags & SSOP_SESDIR) {
PUTLINE_FAIL("exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')");
} else if (ssop_flags & SSOP_CURDIR) {
- sname = home_replace_save(NULL, globaldir != NULL ? (char_u *)globaldir : dirnow);
- char *fname_esc = ses_escape_fname((char *)sname, &ssop_flags);
+ sname = home_replace_save(NULL, globaldir != NULL ? globaldir : (char *)dirnow);
+ char *fname_esc = ses_escape_fname(sname, &ssop_flags);
if (fprintf(fd, "cd %s\n", fname_esc) < 0) {
xfree(fname_esc);
xfree(sname);
@@ -621,7 +621,7 @@ static int makeopens(FILE *fd, char_u *dirnow)
if (fprintf(fd, "badd +%" PRId64 " ",
buf->b_wininfo == NULL
? (int64_t)1L
- : (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0
+ : (int64_t)buf->b_wininfo->wi_mark.mark.lnum) < 0
|| ses_fname(fd, buf, &ssop_flags, true) == FAIL) {
return FAIL;
}
@@ -706,7 +706,7 @@ static int makeopens(FILE *fd, char_u *dirnow)
if (ses_do_win(wp)
&& wp->w_buffer->b_ffname != NULL
&& !bt_help(wp->w_buffer)
- && !bt_nofile(wp->w_buffer)) {
+ && !bt_nofilename(wp->w_buffer)) {
if (need_tabnext && put_line(fd, "tabnext") == FAIL) {
return FAIL;
}
@@ -821,7 +821,7 @@ static int makeopens(FILE *fd, char_u *dirnow)
// Take care of tab-local working directories if applicable
if (tp->tp_localdir) {
if (fputs("if exists(':tcd') == 2 | tcd ", fd) < 0
- || ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL
+ || ses_put_fname(fd, (char_u *)tp->tp_localdir, &ssop_flags) == FAIL
|| fputs(" | endif\n", fd) < 0) {
return FAIL;
}
@@ -1001,7 +1001,7 @@ void ex_mkrc(exarg_T *eap)
*dirnow = NUL;
}
if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) {
- if (vim_chdirfile((char_u *)fname, kCdCauseOther) == OK) {
+ if (vim_chdirfile(fname, kCdCauseOther) == OK) {
shorten_fnames(true);
}
} else if (*dirnow != NUL
@@ -1075,7 +1075,7 @@ static char *get_view_file(int c)
emsg(_(e_noname));
return NULL;
}
- char *sname = (char *)home_replace_save(NULL, curbuf->b_ffname);
+ char *sname = home_replace_save(NULL, curbuf->b_ffname);
// We want a file name without separators, because we're not going to make
// a directory.
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index e327b97fbe..ca276b8a40 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -893,8 +893,7 @@ char_u *vim_findfile(void *search_ctx_arg)
break;
}
assert(MAXPATHL >= len);
- copy_option_part(&suf, file_path + len,
- MAXPATHL - len, ",");
+ copy_option_part((char **)&suf, (char *)file_path + len, MAXPATHL - len, ",");
}
}
} else {
@@ -1503,7 +1502,7 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
break;
}
assert(MAXPATHL >= l);
- copy_option_part(&buf, NameBuff + l, MAXPATHL - l, ",");
+ copy_option_part((char **)&buf, (char *)NameBuff + l, MAXPATHL - l, ",");
}
}
}
@@ -1543,7 +1542,7 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
// copy next path
buf[0] = 0;
- copy_option_part(&dir, buf, MAXPATHL, " ,");
+ copy_option_part((char **)&dir, (char *)buf, MAXPATHL, " ,");
// get the stopdir string
r_ptr = vim_findfile_stopdir(buf);
@@ -1653,12 +1652,12 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause, bool pre
/// Caller must call shorten_fnames()!
///
/// @return OK or FAIL
-int vim_chdirfile(char_u *fname, CdCause cause)
+int vim_chdirfile(char *fname, CdCause cause)
{
char dir[MAXPATHL];
STRLCPY(dir, fname, MAXPATHL);
- *path_tail_with_sep((char_u *)dir) = NUL;
+ *path_tail_with_sep(dir) = NUL;
if (os_dirname(NameBuff, sizeof(NameBuff)) != OK) {
NameBuff[0] = NUL;
@@ -1688,7 +1687,7 @@ int vim_chdirfile(char_u *fname, CdCause cause)
int vim_chdir(char_u *new_dir)
{
char *dir_name = (char *)find_directory_in_path(new_dir, STRLEN(new_dir),
- FNAME_MESS, curbuf->b_ffname);
+ FNAME_MESS, (char_u *)curbuf->b_ffname);
if (dir_name == NULL) {
return -1;
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 9ba55befdd..6782465ef1 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -275,10 +275,9 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
// executing nasty autocommands. Also check if "fname" and "sfname"
// point to one of these values.
old_curbuf = curbuf;
- old_b_ffname = (char *)curbuf->b_ffname;
+ old_b_ffname = curbuf->b_ffname;
old_b_fname = curbuf->b_fname;
- using_b_ffname = ((char_u *)fname == curbuf->b_ffname)
- || ((char_u *)sfname == curbuf->b_ffname);
+ using_b_ffname = (fname == curbuf->b_ffname) || (sfname == curbuf->b_ffname);
using_b_fname = (fname == curbuf->b_fname) || (sfname == curbuf->b_fname);
// After reading a file the cursor line changes but we don't want to
@@ -373,7 +372,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
&& !S_ISFIFO(perm) // ... or fifo
&& !S_ISSOCK(perm) // ... or socket
#ifdef OPEN_CHR_FILES
- && !(S_ISCHR(perm) && is_dev_fd_file((char_u *)fname))
+ && !(S_ISCHR(perm) && is_dev_fd_file(fname))
// ... or a character special file named /dev/fd/<n>
#endif
) {
@@ -466,7 +465,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
// SwapExists autocommand may mess things up
if (curbuf != old_curbuf
|| (using_b_ffname
- && ((char_u *)old_b_ffname != curbuf->b_ffname))
+ && (old_b_ffname != curbuf->b_ffname))
|| (using_b_fname
&& (old_b_fname != curbuf->b_fname))) {
emsg(_(e_auchangedbuf));
@@ -538,7 +537,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
check_need_swap(newfile);
if (!read_stdin
&& (curbuf != old_curbuf
- || (using_b_ffname && ((char_u *)old_b_ffname != curbuf->b_ffname))
+ || (using_b_ffname && (old_b_ffname != curbuf->b_ffname))
|| (using_b_fname && (old_b_fname != curbuf->b_fname)))) {
emsg(_(e_auchangedbuf));
if (!read_buffer) {
@@ -645,7 +644,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
* (cd for example) if it invalidates fname or sfname.
*/
if (!read_stdin && (curbuf != old_curbuf
- || (using_b_ffname && ((char_u *)old_b_ffname != curbuf->b_ffname))
+ || (using_b_ffname && (old_b_ffname != curbuf->b_ffname))
|| (using_b_fname && (old_b_fname != curbuf->b_fname))
|| (fd = os_open(fname, O_RDONLY, 0)) < 0)) {
no_wait_return--;
@@ -1974,12 +1973,12 @@ failed:
/// Do not accept "/dev/fd/[012]", opening these may hang Vim.
///
/// @param fname file name to check
-bool is_dev_fd_file(char_u *fname)
+bool is_dev_fd_file(char *fname)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
return STRNCMP(fname, "/dev/fd/", 8) == 0
- && ascii_isdigit(fname[8])
- && *skipdigits((char *)fname + 9) == NUL
+ && ascii_isdigit((uint8_t)fname[8])
+ && *skipdigits(fname + 9) == NUL
&& (fname[9] != NUL
|| (fname[8] != '0' && fname[8] != '1' && fname[8] != '2'));
}
@@ -2287,7 +2286,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
&& reset_changed
&& whole
&& buf == curbuf
- && !bt_nofile(buf)
+ && !bt_nofilename(buf)
&& !filtering
&& (!append || vim_strchr(p_cpo, CPO_FNAMEAPP) != NULL)
&& vim_strchr(p_cpo, CPO_FNAMEW) != NULL) {
@@ -2342,16 +2341,16 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
* Set curbuf to the buffer to be written.
* Careful: The autocommands may call buf_write() recursively!
*/
- if ((char_u *)ffname == buf->b_ffname) {
+ if (ffname == buf->b_ffname) {
buf_ffname = true;
}
- if ((char_u *)sfname == buf->b_sfname) {
+ if (sfname == buf->b_sfname) {
buf_sfname = true;
}
- if ((char_u *)fname == buf->b_ffname) {
+ if (fname == buf->b_ffname) {
buf_fname_f = true;
}
- if ((char_u *)fname == buf->b_sfname) {
+ if (fname == buf->b_sfname) {
buf_fname_s = true;
}
@@ -2361,22 +2360,22 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if (append) {
if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEAPPENDCMD,
- sfname, sfname, FALSE, curbuf, eap))) {
- if (overwriting && bt_nofile(curbuf)) {
- nofile_err = TRUE;
+ sfname, sfname, false, curbuf, eap))) {
+ if (overwriting && bt_nofilename(curbuf)) {
+ nofile_err = true;
} else {
apply_autocmds_exarg(EVENT_FILEAPPENDPRE,
- sfname, sfname, FALSE, curbuf, eap);
+ sfname, sfname, false, curbuf, eap);
}
}
} else if (filtering) {
apply_autocmds_exarg(EVENT_FILTERWRITEPRE,
- NULL, sfname, FALSE, curbuf, eap);
+ NULL, sfname, false, curbuf, eap);
} else if (reset_changed && whole) {
int was_changed = curbufIsChanged();
did_cmd = apply_autocmds_exarg(EVENT_BUFWRITECMD,
- sfname, sfname, FALSE, curbuf, eap);
+ sfname, sfname, false, curbuf, eap);
if (did_cmd) {
if (was_changed && !curbufIsChanged()) {
/* Written everything correctly and BufWriteCmd has reset
@@ -2386,21 +2385,21 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
u_update_save_nr(curbuf);
}
} else {
- if (overwriting && bt_nofile(curbuf)) {
- nofile_err = TRUE;
+ if (overwriting && bt_nofilename(curbuf)) {
+ nofile_err = true;
} else {
apply_autocmds_exarg(EVENT_BUFWRITEPRE,
- sfname, sfname, FALSE, curbuf, eap);
+ sfname, sfname, false, curbuf, eap);
}
}
} else {
if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEWRITECMD,
- sfname, sfname, FALSE, curbuf, eap))) {
- if (overwriting && bt_nofile(curbuf)) {
- nofile_err = TRUE;
+ sfname, sfname, false, curbuf, eap))) {
+ if (overwriting && bt_nofilename(curbuf)) {
+ nofile_err = true;
} else {
apply_autocmds_exarg(EVENT_FILEWRITEPRE,
- sfname, sfname, FALSE, curbuf, eap);
+ sfname, sfname, false, curbuf, eap);
}
}
}
@@ -2491,16 +2490,16 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
* be kept in fname, ffname and sfname.
*/
if (buf_ffname) {
- ffname = (char *)buf->b_ffname;
+ ffname = buf->b_ffname;
}
if (buf_sfname) {
- sfname = (char *)buf->b_sfname;
+ sfname = buf->b_sfname;
}
if (buf_fname_f) {
- fname = (char *)buf->b_ffname;
+ fname = buf->b_ffname;
}
if (buf_fname_s) {
- fname = (char *)buf->b_sfname;
+ fname = buf->b_sfname;
}
}
@@ -2762,7 +2761,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
/*
* Isolate one directory name, using an entry in 'bdir'.
*/
- size_t dir_len = copy_option_part((char_u **)&dirp, IObuff, IOSIZE, ",");
+ size_t dir_len = copy_option_part(&dirp, (char *)IObuff, IOSIZE, ",");
p = (char *)IObuff + dir_len;
bool trailing_pathseps = after_pathsep((char *)IObuff, p) && p[-1] == p[-2];
if (trailing_pathseps) {
@@ -2923,7 +2922,7 @@ nobackup:
/*
* Isolate one directory name and make the backup file name.
*/
- size_t dir_len = copy_option_part((char_u **)&dirp, IObuff, IOSIZE, ",");
+ size_t dir_len = copy_option_part(&dirp, (char *)IObuff, IOSIZE, ",");
p = (char *)IObuff + dir_len;
bool trailing_pathseps = after_pathsep((char *)IObuff, p) && p[-1] == p[-2];
if (trailing_pathseps) {
@@ -4304,24 +4303,24 @@ static int make_bom(char_u *buf, char_u *name)
/// name.
void shorten_buf_fname(buf_T *buf, char_u *dirname, int force)
{
- char_u *p;
+ char *p;
if (buf->b_fname != NULL
- && !bt_nofile(buf)
+ && !bt_nofilename(buf)
&& !path_with_url(buf->b_fname)
&& (force
|| buf->b_sfname == NULL
- || path_is_absolute(buf->b_sfname))) {
+ || path_is_absolute((char_u *)buf->b_sfname))) {
if (buf->b_sfname != buf->b_ffname) {
XFREE_CLEAR(buf->b_sfname);
}
- p = path_shorten_fname(buf->b_ffname, dirname);
+ p = (char *)path_shorten_fname((char_u *)buf->b_ffname, dirname);
if (p != NULL) {
- buf->b_sfname = vim_strsave(p);
- buf->b_fname = (char *)buf->b_sfname;
+ buf->b_sfname = xstrdup(p);
+ buf->b_fname = buf->b_sfname;
}
if (p == NULL) {
- buf->b_fname = (char *)buf->b_ffname;
+ buf->b_fname = buf->b_ffname;
}
}
}
@@ -4918,7 +4917,7 @@ int buf_check_timestamp(buf_T *buf)
bool file_info_ok;
if (!(buf->b_flags & BF_NOTEDITED)
&& buf->b_mtime != 0
- && (!(file_info_ok = os_fileinfo((char *)buf->b_ffname, &file_info))
+ && (!(file_info_ok = os_fileinfo(buf->b_ffname, &file_info))
|| time_differs(&file_info, buf->b_mtime, buf->b_mtime_ns)
|| (int)file_info.stat.st_mode != buf->b_orig_mode)) {
const long prev_b_mtime = buf->b_mtime;
@@ -5016,7 +5015,7 @@ int buf_check_timestamp(buf_T *buf)
}
}
} else if ((buf->b_flags & BF_NEW) && !(buf->b_flags & BF_NEW_W)
- && os_path_exists(buf->b_ffname)) {
+ && os_path_exists((char_u *)buf->b_ffname)) {
retval = 1;
mesg = _("W13: Warning: File \"%s\" has been created after editing started");
buf->b_flags |= BF_NEW_W;
@@ -5024,7 +5023,7 @@ int buf_check_timestamp(buf_T *buf)
}
if (mesg != NULL) {
- path = home_replace_save(buf, (char_u *)buf->b_fname);
+ path = (char_u *)home_replace_save(buf, buf->b_fname);
if (!helpmesg) {
mesg2 = "";
}
@@ -5169,7 +5168,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options)
if (saved == OK) {
curbuf->b_flags |= BF_CHECK_RO; // check for RO again
keep_filetype = true; // don't detect 'filetype'
- if (readfile((char *)buf->b_ffname, buf->b_fname, (linenr_T)0, (linenr_T)0,
+ if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0, (linenr_T)0,
(linenr_T)MAXLNUM, &ea, flags, false) != OK) {
if (!aborting()) {
semsg(_("E321: Could not reload \"%s\""), buf->b_fname);
@@ -5547,10 +5546,10 @@ bool match_file_pat(char *pattern, regprog_T **prog, char *fname, char *sfname,
*/
if (regmatch.regprog != NULL
&& ((allow_dirs
- && (vim_regexec(&regmatch, (char_u *)fname, (colnr_T)0)
+ && (vim_regexec(&regmatch, fname, (colnr_T)0)
|| (sfname != NULL
- && vim_regexec(&regmatch, (char_u *)sfname, (colnr_T)0))))
- || (!allow_dirs && vim_regexec(&regmatch, (char_u *)tail, (colnr_T)0)))) {
+ && vim_regexec(&regmatch, sfname, (colnr_T)0))))
+ || (!allow_dirs && vim_regexec(&regmatch, tail, (colnr_T)0)))) {
result = true;
}
@@ -5586,7 +5585,7 @@ bool match_file_list(char_u *list, char_u *sfname, char_u *ffname)
// try all patterns in 'wildignore'
p = list;
while (*p) {
- copy_option_part(&p, buf, ARRAY_SIZE(buf), ",");
+ copy_option_part((char **)&p, (char *)buf, ARRAY_SIZE(buf), ",");
regpat = (char_u *)file_pat_to_reg_pat((char *)buf, NULL, &allow_dirs, false);
if (regpat == NULL) {
break;
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 3c3aaa1031..8f26e03a94 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -3,9 +3,7 @@
// vim: set fdm=marker fdl=1 fdc=3
-/*
- * fold.c: code for folding
- */
+// fold.c: code for folding
#include <inttypes.h>
#include <string.h>
@@ -41,12 +39,12 @@
// local declarations. {{{1
// typedef fold_T {{{2
-/*
- * The toplevel folds for each window are stored in the w_folds growarray.
- * Each toplevel fold can contain an array of second level folds in the
- * fd_nested growarray.
- * The info stored in both growarrays is the same: An array of fold_T.
- */
+
+// The toplevel folds for each window are stored in the w_folds growarray.
+// Each toplevel fold can contain an array of second level folds in the
+// fd_nested growarray.
+// The info stored in both growarrays is the same: An array of fold_T.
+
typedef struct {
linenr_T fd_top; // first line of fold; for nested fold
// relative to parent
@@ -93,20 +91,16 @@ typedef void (*LevelGetter)(fline_T *);
#endif
static char *e_nofold = N_("E490: No fold found");
-/*
- * While updating the folds lines between invalid_top and invalid_bot have an
- * undefined fold level. Only used for the window currently being updated.
- */
+// While updating the folds lines between invalid_top and invalid_bot have an
+// undefined fold level. Only used for the window currently being updated.
static linenr_T invalid_top = (linenr_T)0;
static linenr_T invalid_bot = (linenr_T)0;
-/*
- * When using 'foldexpr' we sometimes get the level of the next line, which
- * calls foldlevel() to get the level of the current line, which hasn't been
- * stored yet. To get around this chicken-egg problem the level of the
- * previous line is stored here when available. prev_lnum is zero when the
- * level is not available.
- */
+// When using 'foldexpr' we sometimes get the level of the next line, which
+// calls foldlevel() to get the level of the current line, which hasn't been
+// stored yet. To get around this chicken-egg problem the level of the
+// previous line is stored here when available. prev_lnum is zero when the
+// level is not available.
static linenr_T prev_lnum = 0;
static int prev_lnum_lvl = -1;
@@ -130,7 +124,7 @@ void copyFoldingState(win_T *wp_from, win_T *wp_to)
}
// hasAnyFolding() {{{2
-/// @return TRUE if there may be folded lines in the current window.
+/// @return true if there may be folded lines in the current window.
int hasAnyFolding(win_T *win)
{
// very simple now, but can become more complex later
@@ -160,16 +154,6 @@ bool hasFolding(linenr_T lnum, linenr_T *firstp, linenr_T *lastp)
bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp,
linenr_T *const lastp, const bool cache, foldinfo_T *const infop)
{
- bool had_folded = false;
- linenr_T first = 0;
- linenr_T last = 0;
- linenr_T lnum_rel = lnum;
- fold_T *fp;
- int level = 0;
- bool use_level = false;
- bool maybe_small = false;
- int low_level = 0;
-
checkupdate(win);
// Return quickly when there is no folding at all in this window.
@@ -180,11 +164,13 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp
return false;
}
+ bool had_folded = false;
+ linenr_T first = 0;
+ linenr_T last = 0;
+
if (cache) {
- /*
- * First look in cached info for displayed lines. This is probably
- * the fastest, but it can only be used if the entry is still valid.
- */
+ // First look in cached info for displayed lines. This is probably
+ // the fastest, but it can only be used if the entry is still valid.
const int x = find_wl_entry(win, lnum);
if (x >= 0) {
first = win->w_lines[x].wl_lnum;
@@ -193,10 +179,15 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp
}
}
+ linenr_T lnum_rel = lnum;
+ int level = 0;
+ int low_level = 0;
+ fold_T *fp;
+ bool maybe_small = false;
+ bool use_level = false;
+
if (first == 0) {
- /*
- * Recursively search for a fold that contains "lnum".
- */
+ // Recursively search for a fold that contains "lnum".
garray_T *gap = &win->w_folds;
for (;;) {
if (!foldFind(gap, lnum_rel, &fp)) {
@@ -224,7 +215,7 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp
// relative to containing fold.
gap = &fp->fd_nested;
lnum_rel -= fp->fd_top;
- ++level;
+ level++;
}
}
@@ -311,42 +302,42 @@ foldinfo_T fold_info(win_T *win, linenr_T lnum)
}
// foldmethodIsManual() {{{2
-/// @return TRUE if 'foldmethod' is "manual"
+/// @return true if 'foldmethod' is "manual"
int foldmethodIsManual(win_T *wp)
{
return wp->w_p_fdm[3] == 'u';
}
// foldmethodIsIndent() {{{2
-/// @return TRUE if 'foldmethod' is "indent"
+/// @return true if 'foldmethod' is "indent"
int foldmethodIsIndent(win_T *wp)
{
return wp->w_p_fdm[0] == 'i';
}
// foldmethodIsExpr() {{{2
-/// @return TRUE if 'foldmethod' is "expr"
+/// @return true if 'foldmethod' is "expr"
int foldmethodIsExpr(win_T *wp)
{
return wp->w_p_fdm[1] == 'x';
}
// foldmethodIsMarker() {{{2
-/// @return TRUE if 'foldmethod' is "marker"
+/// @return true if 'foldmethod' is "marker"
int foldmethodIsMarker(win_T *wp)
{
return wp->w_p_fdm[2] == 'r';
}
// foldmethodIsSyntax() {{{2
-/// @return TRUE if 'foldmethod' is "syntax"
+/// @return true if 'foldmethod' is "syntax"
int foldmethodIsSyntax(win_T *wp)
{
return wp->w_p_fdm[0] == 's';
}
// foldmethodIsDiff() {{{2
-/// @return TRUE if 'foldmethod' is "diff"
+/// @return true if 'foldmethod' is "diff"
int foldmethodIsDiff(win_T *wp)
{
return wp->w_p_fdm[0] == 'd';
@@ -372,18 +363,17 @@ void closeFoldRecurse(pos_T pos)
/// Open or Close folds for current window in lines "first" to "last".
/// Used for "zo", "zO", "zc" and "zC" in Visual mode.
///
-/// @param opening TRUE to open, FALSE to close
-/// @param recurse TRUE to do it recursively
-/// @param had_visual TRUE when Visual selection used
+/// @param opening true to open, false to close
+/// @param recurse true to do it recursively
+/// @param had_visual true when Visual selection used
void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, int had_visual)
{
int done = DONE_NOTHING; // avoid error messages
linenr_T first = firstpos.lnum;
linenr_T last = lastpos.lnum;
- linenr_T lnum;
linenr_T lnum_next;
- for (lnum = first; lnum <= last; lnum = lnum_next + 1) {
+ for (linenr_T lnum = first; lnum <= last; lnum = lnum_next + 1) {
pos_T temp = { lnum, 0, 0 };
lnum_next = lnum;
// Opening one level only: next fold to open is after the one going to
@@ -426,12 +416,10 @@ void openFoldRecurse(pos_T pos)
/// Open folds until the cursor line is not in a closed fold.
void foldOpenCursor(void)
{
- int done;
-
checkupdate(curwin);
if (hasAnyFolding(curwin)) {
for (;;) {
- done = DONE_NOTHING;
+ int done = DONE_NOTHING;
(void)setManualFold(curwin->w_cursor, true, false, &done);
if (!(done & DONE_ACTION)) {
break;
@@ -447,9 +435,7 @@ void newFoldLevel(void)
newFoldLevelWin(curwin);
if (foldmethodIsDiff(curwin) && curwin->w_p_scb) {
- /*
- * Set the same foldlevel in other windows in diff mode.
- */
+ // Set the same foldlevel in other windows in diff mode.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) {
wp->w_p_fdl = curwin->w_p_fdl;
@@ -461,15 +447,13 @@ void newFoldLevel(void)
static void newFoldLevelWin(win_T *wp)
{
- fold_T *fp;
-
checkupdate(wp);
if (wp->w_fold_manual) {
// Set all flags for the first level of folds to FD_LEVEL. Following
// manual open/close will then change the flags to FD_OPEN or
// FD_CLOSED for those folds that don't use 'foldlevel'.
- fp = (fold_T *)wp->w_folds.ga_data;
- for (int i = 0; i < wp->w_folds.ga_len; ++i) {
+ fold_T *fp = (fold_T *)wp->w_folds.ga_data;
+ for (int i = 0; i < wp->w_folds.ga_len; i++) {
fp[i].fd_flags = FD_LEVEL;
}
wp->w_fold_manual = false;
@@ -491,19 +475,18 @@ void foldCheckClose(void)
}
// checkCloseRec() {{{2
-static int checkCloseRec(garray_T *gap, linenr_T lnum, int level)
+static bool checkCloseRec(garray_T *gap, linenr_T lnum, int level)
{
- fold_T *fp;
- int retval = FALSE;
+ bool retval = false;
- fp = (fold_T *)gap->ga_data;
- for (int i = 0; i < gap->ga_len; ++i) {
+ fold_T *fp = (fold_T *)gap->ga_data;
+ for (int i = 0; i < gap->ga_len; i++) {
// Only manually opened folds may need to be closed.
if (fp[i].fd_flags == FD_OPEN) {
if (level <= 0 && (lnum < fp[i].fd_top
|| lnum >= fp[i].fd_top + fp[i].fd_len)) {
fp[i].fd_flags = FD_LEVEL;
- retval = TRUE;
+ retval = true;
} else {
retval |= checkCloseRec(&fp[i].fd_nested, lnum - fp[i].fd_top,
level - 1);
@@ -514,19 +497,19 @@ static int checkCloseRec(garray_T *gap, linenr_T lnum, int level)
}
// foldCreateAllowed() {{{2
-/// @return TRUE if it's allowed to manually create or delete a fold or,
-/// give an error message and return FALSE if not.
+/// @return true if it's allowed to manually create or delete a fold or,
+/// give an error message and return false if not.
int foldManualAllowed(bool create)
{
if (foldmethodIsManual(curwin) || foldmethodIsMarker(curwin)) {
- return TRUE;
+ return true;
}
if (create) {
emsg(_("E350: Cannot create fold with current 'foldmethod'"));
} else {
emsg(_("E351: Cannot delete fold with current 'foldmethod'"));
}
- return FALSE;
+ return false;
}
// foldCreate() {{{2
@@ -534,13 +517,8 @@ int foldManualAllowed(bool create)
/// window.
void foldCreate(win_T *wp, pos_T start, pos_T end)
{
- fold_T *fp;
- garray_T *gap;
- garray_T fold_ga;
- int i;
- int cont;
- int use_level = FALSE;
- int closed = FALSE;
+ int use_level = false;
+ int closed = false;
int level = 0;
pos_T start_rel = start;
pos_T end_rel = end;
@@ -561,11 +539,14 @@ void foldCreate(win_T *wp, pos_T start, pos_T end)
checkupdate(wp);
+ int i;
+
// Find the place to insert the new fold
- gap = &wp->w_folds;
+ garray_T *gap = &wp->w_folds;
if (gap->ga_len == 0) {
i = 0;
} else {
+ fold_T *fp;
for (;;) {
if (!foldFind(gap, start_rel.lnum, &fp)) {
break;
@@ -599,10 +580,12 @@ void foldCreate(win_T *wp, pos_T start, pos_T end)
ga_grow(gap, 1);
{
- fp = (fold_T *)gap->ga_data + i;
+ fold_T *fp = (fold_T *)gap->ga_data + i;
+ garray_T fold_ga;
ga_init(&fold_ga, (int)sizeof(fold_T), 10);
// Count number of folds that will be contained in the new fold.
+ int cont;
for (cont = 0; i + cont < gap->ga_len; cont++) {
if (fp[cont].fd_top > end_rel.lnum) {
break;
@@ -668,7 +651,6 @@ void foldCreate(win_T *wp, pos_T start, pos_T end)
void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const int recursive,
const bool had_visual)
{
- fold_T *fp;
fold_T *found_fp = NULL;
linenr_T found_off = 0;
bool maybe_small = false;
@@ -687,6 +669,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const
linenr_T lnum_off = 0;
bool use_level = false;
for (;;) {
+ fold_T *fp;
if (!foldFind(gap, lnum - lnum_off, &fp)) {
break;
}
@@ -704,10 +687,10 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const
// check nested folds
gap = &fp->fd_nested;
lnum_off += fp->fd_top;
- ++level;
+ level++;
}
if (found_ga == NULL) {
- ++lnum;
+ lnum++;
} else {
lnum = found_fp->fd_top + found_fp->fd_len + found_off;
@@ -808,7 +791,7 @@ void foldUpdate(win_T *wp, linenr_T top, linenr_T bot)
int save_got_int = got_int;
// reset got_int here, otherwise it won't work
- got_int = FALSE;
+ got_int = false;
foldUpdateIEMS(wp, top, bot);
got_int |= save_got_int;
}
@@ -944,7 +927,7 @@ int foldMoveTo(const bool updown, const int dir, const long count)
// Check nested folds (if any).
gap = &fp->fd_nested;
lnum_off += fp->fd_top;
- ++level;
+ level++;
}
if (lnum_found != curwin->w_cursor.lnum) {
if (retval == FAIL) {
@@ -970,15 +953,13 @@ void foldInitWin(win_T *new_win)
// find_wl_entry() {{{2
/// Find an entry in the win->w_lines[] array for buffer line "lnum".
-/// Only valid entries are considered (for entries where wl_valid is FALSE the
+/// Only valid entries are considered (for entries where wl_valid is false the
/// line number can be wrong).
///
/// @return index of entry or -1 if not found.
int find_wl_entry(win_T *win, linenr_T lnum)
{
- int i;
-
- for (i = 0; i < win->w_lines_valid; ++i) {
+ for (int i = 0; i < win->w_lines_valid; i++) {
if (win->w_lines[i].wl_valid) {
if (lnum < win->w_lines[i].wl_lnum) {
return -1;
@@ -995,13 +976,13 @@ int find_wl_entry(win_T *win, linenr_T lnum)
/// Adjust the Visual area to include any fold at the start or end completely.
void foldAdjustVisual(void)
{
- pos_T *start, *end;
- char_u *ptr;
-
if (!VIsual_active || !hasAnyFolding(curwin)) {
return;
}
+ pos_T *start, *end;
+ char_u *ptr;
+
if (ltoreq(VIsual, curwin->w_cursor)) {
start = &VIsual;
end = &curwin->w_cursor;
@@ -1035,9 +1016,6 @@ void foldAdjustCursor(void)
/// Will "clone" (i.e deep copy) a garray_T of folds.
void cloneFoldGrowArray(garray_T *from, garray_T *to)
{
- fold_T *from_p;
- fold_T *to_p;
-
ga_init(to, from->ga_itemsize, from->ga_growsize);
if (GA_EMPTY(from)) {
@@ -1046,8 +1024,8 @@ void cloneFoldGrowArray(garray_T *from, garray_T *to)
ga_grow(to, from->ga_len);
- from_p = (fold_T *)from->ga_data;
- to_p = (fold_T *)to->ga_data;
+ fold_T *from_p = (fold_T *)from->ga_data;
+ fold_T *to_p = (fold_T *)to->ga_data;
for (int i = 0; i < from->ga_len; i++) {
to_p->fd_top = from_p->fd_top;
@@ -1055,9 +1033,9 @@ void cloneFoldGrowArray(garray_T *from, garray_T *to)
to_p->fd_flags = from_p->fd_flags;
to_p->fd_small = from_p->fd_small;
cloneFoldGrowArray(&from_p->fd_nested, &to_p->fd_nested);
- ++to->ga_len;
- ++from_p;
- ++to_p;
+ to->ga_len++;
+ from_p++;
+ to_p++;
}
}
@@ -1069,22 +1047,17 @@ void cloneFoldGrowArray(garray_T *from, garray_T *to)
/// @return false when there is no fold that contains "lnum".
static bool foldFind(const garray_T *gap, linenr_T lnum, fold_T **fpp)
{
- linenr_T low, high;
- fold_T *fp;
-
if (gap->ga_len == 0) {
*fpp = NULL;
return false;
}
- /*
- * Perform a binary search.
- * "low" is lowest index of possible match.
- * "high" is highest index of possible match.
- */
- fp = (fold_T *)gap->ga_data;
- low = 0;
- high = gap->ga_len - 1;
+ // Perform a binary search.
+ // "low" is lowest index of possible match.
+ // "high" is highest index of possible match.
+ fold_T *fp = (fold_T *)gap->ga_data;
+ linenr_T low = 0;
+ linenr_T high = gap->ga_len - 1;
while (low <= high) {
linenr_T i = (low + high) / 2;
if (fp[i].fd_top > lnum) {
@@ -1110,10 +1083,9 @@ static int foldLevelWin(win_T *wp, linenr_T lnum)
fold_T *fp;
linenr_T lnum_rel = lnum;
int level = 0;
- garray_T *gap;
// Recursively search for a fold that contains "lnum".
- gap = &wp->w_folds;
+ garray_T *gap = &wp->w_folds;
for (;;) {
if (!foldFind(gap, lnum_rel, &fp)) {
break;
@@ -1121,7 +1093,7 @@ static int foldLevelWin(win_T *wp, linenr_T lnum)
// Check nested folds. Line number is relative to containing fold.
gap = &fp->fd_nested;
lnum_rel -= fp->fd_top;
- ++level;
+ level++;
}
return level;
@@ -1142,11 +1114,8 @@ static void checkupdate(win_T *wp)
/// Repeat "count" times.
static void setFoldRepeat(pos_T pos, long count, int do_open)
{
- int done;
- long n;
-
- for (n = 0; n < count; ++n) {
- done = DONE_NOTHING;
+ for (int n = 0; n < count; n++) {
+ int done = DONE_NOTHING;
(void)setManualFold(pos, do_open, false, &done);
if (!(done & DONE_ACTION)) {
// Only give an error message when no fold could be opened.
@@ -1162,18 +1131,15 @@ static void setFoldRepeat(pos_T pos, long count, int do_open)
/// Open or close the fold in the current window which contains "lnum".
/// Also does this for other windows in diff mode when needed.
///
-/// @param opening TRUE when opening, FALSE when closing
-/// @param recurse TRUE when closing/opening recursive
+/// @param opening true when opening, false when closing
+/// @param recurse true when closing/opening recursive
static linenr_T setManualFold(pos_T pos, int opening, int recurse, int *donep)
{
- linenr_T lnum = pos.lnum;
if (foldmethodIsDiff(curwin) && curwin->w_p_scb) {
linenr_T dlnum;
- /*
- * Do the same operation in other windows in diff mode. Calculate the
- * line number from the diffs.
- */
+ // Do the same operation in other windows in diff mode. Calculate the
+ // line number from the diffs.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) {
dlnum = diff_lnum_win(curwin->w_cursor.lnum, wp);
@@ -1184,7 +1150,7 @@ static linenr_T setManualFold(pos_T pos, int opening, int recurse, int *donep)
}
}
- return setManualFoldWin(curwin, lnum, opening, recurse, donep);
+ return setManualFoldWin(curwin, pos.lnum, opening, recurse, donep);
}
// setManualFoldWin() {{{2
@@ -1194,31 +1160,27 @@ static linenr_T setManualFold(pos_T pos, int opening, int recurse, int *donep)
/// When "donep" is NULL give an error message when no fold was found for
/// "lnum", but only if "wp" is "curwin".
///
-/// @param opening TRUE when opening, FALSE when closing
-/// @param recurse TRUE when closing/opening recursive
+/// @param opening true when opening, false when closing
+/// @param recurse true when closing/opening recursive
///
/// @return the line number of the next line that could be closed.
-/// It's only valid when "opening" is TRUE!
+/// It's only valid when "opening" is true!
static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recurse, int *donep)
{
fold_T *fp;
fold_T *fp2;
fold_T *found = NULL;
- int j;
int level = 0;
- int use_level = FALSE;
- int found_fold = FALSE;
- garray_T *gap;
+ bool use_level = false;
+ bool found_fold = false;
linenr_T next = MAXLNUM;
linenr_T off = 0;
int done = 0;
checkupdate(wp);
- /*
- * Find the fold, open or close it.
- */
- gap = &wp->w_folds;
+ // Find the fold, open or close it.
+ garray_T *gap = &wp->w_folds;
for (;;) {
if (!foldFind(gap, lnum, &fp)) {
// If there is a following fold, continue there next time.
@@ -1229,7 +1191,7 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recu
}
// lnum is inside this fold
- found_fold = TRUE;
+ found_fold = true;
// If there is a following fold, continue there next time.
if (fp + 1 < (fold_T *)gap->ga_data + gap->ga_len) {
@@ -1238,14 +1200,14 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recu
// Change from level-dependent folding to manual.
if (use_level || fp->fd_flags == FD_LEVEL) {
- use_level = TRUE;
+ use_level = true;
if (level >= wp->w_p_fdl) {
fp->fd_flags = FD_CLOSED;
} else {
fp->fd_flags = FD_OPEN;
}
fp2 = (fold_T *)fp->fd_nested.ga_data;
- for (j = 0; j < fp->fd_nested.ga_len; ++j) {
+ for (int j = 0; j < fp->fd_nested.ga_len; j++) {
fp2[j].fd_flags = FD_LEVEL;
}
}
@@ -1273,7 +1235,7 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recu
gap = &fp->fd_nested;
lnum -= fp->fd_top;
off += fp->fd_top;
- ++level;
+ level++;
}
if (found_fold) {
// When closing and not recurse, close deepest open fold.
@@ -1301,10 +1263,8 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recu
/// Open all nested folds in fold "fpr" recursively.
static void foldOpenNested(fold_T *fpr)
{
- fold_T *fp;
-
- fp = (fold_T *)fpr->fd_nested.ga_data;
- for (int i = 0; i < fpr->fd_nested.ga_len; ++i) {
+ fold_T *fp = (fold_T *)fpr->fd_nested.ga_data;
+ for (int i = 0; i < fpr->fd_nested.ga_len; i++) {
foldOpenNested(&fp[i]);
fp[i].fd_flags = FD_OPEN;
}
@@ -1390,14 +1350,12 @@ void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount,
static void foldMarkAdjustRecurse(win_T *wp, garray_T *gap, linenr_T line1, linenr_T line2,
linenr_T amount, linenr_T amount_after)
{
- fold_T *fp;
- linenr_T last;
- linenr_T top;
-
if (gap->ga_len == 0) {
return;
}
+ linenr_T top;
+
// In Insert mode an inserted line at the top of a fold is considered part
// of the fold, otherwise it isn't.
if ((State & MODE_INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM) {
@@ -1407,25 +1365,22 @@ static void foldMarkAdjustRecurse(win_T *wp, garray_T *gap, linenr_T line1, line
}
// Find the fold containing or just below "line1".
+ fold_T *fp;
(void)foldFind(gap, line1, &fp);
- /*
- * Adjust all folds below "line1" that are affected.
- */
- for (int i = (int)(fp - (fold_T *)gap->ga_data); i < gap->ga_len; ++i, ++fp) {
- /*
- * Check for these situations:
- * 1 2 3
- * 1 2 3
- * line1 2 3 4 5
- * 2 3 4 5
- * 2 3 4 5
- * line2 2 3 4 5
- * 3 5 6
- * 3 5 6
- */
-
- last = fp->fd_top + fp->fd_len - 1; // last line of fold
+ // Adjust all folds below "line1" that are affected.
+ for (int i = (int)(fp - (fold_T *)gap->ga_data); i < gap->ga_len; i++, fp++) {
+ // Check for these situations:
+ // 1 2 3
+ // 1 2 3
+ // line1 2 3 4 5
+ // 2 3 4 5
+ // 2 3 4 5
+ // line2 2 3 4 5
+ // 3 5 6
+ // 3 5 6
+
+ linenr_T last = fp->fd_top + fp->fd_len - 1; // last line of fold
// 1. fold completely above line1: nothing to do
if (last < line1) {
@@ -1498,13 +1453,11 @@ int getDeepestNesting(win_T *wp)
static int getDeepestNestingRecurse(garray_T *gap)
{
- int level;
int maxlevel = 0;
- fold_T *fp;
- fp = (fold_T *)gap->ga_data;
- for (int i = 0; i < gap->ga_len; ++i) {
- level = getDeepestNestingRecurse(&fp[i].fd_nested) + 1;
+ fold_T *fp = (fold_T *)gap->ga_data;
+ for (int i = 0; i < gap->ga_len; i++) {
+ int level = getDeepestNestingRecurse(&fp[i].fd_nested) + 1;
if (level > maxlevel) {
maxlevel = level;
}
@@ -1622,14 +1575,13 @@ static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end)
static void foldAddMarker(buf_T *buf, pos_T pos, const char_u *marker, size_t markerlen)
{
char_u *cms = buf->b_p_cms;
- char_u *line;
char_u *newline;
char_u *p = (char_u *)strstr((char *)buf->b_p_cms, "%s");
bool line_is_comment = false;
linenr_T lnum = pos.lnum;
// Allocate a new line: old-line + 'cms'-start + marker + 'cms'-end
- line = ml_get_buf(buf, lnum, false);
+ char_u *line = ml_get_buf(buf, lnum, false);
size_t line_len = STRLEN(line);
size_t added = 0;
@@ -1681,14 +1633,12 @@ static void deleteFoldMarkers(win_T *wp, fold_T *fp, int recursive, linenr_T lnu
/// close-marker.
static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t markerlen)
{
- char_u *newline;
- char_u *cms = buf->b_p_cms;
- char_u *cms2;
-
// end marker may be missing and fold extends below the last line
if (lnum > buf->b_ml.ml_line_count) {
return;
}
+
+ char_u *cms = buf->b_p_cms;
char_u *line = ml_get_buf(buf, lnum, false);
for (char_u *p = line; *p != NUL; p++) {
if (STRNCMP(p, marker, markerlen) != 0) {
@@ -1697,11 +1647,11 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t mark
// Found the marker, include a digit if it's there.
size_t len = markerlen;
if (ascii_isdigit(p[len])) {
- ++len;
+ len++;
}
if (*cms != NUL) {
// Also delete 'commentstring' if it matches.
- cms2 = (char_u *)strstr((char *)cms, "%s");
+ char_u *cms2 = (char_u *)strstr((char *)cms, "%s");
if (p - line >= cms2 - cms
&& STRNCMP(p - (cms2 - cms), cms, cms2 - cms) == 0
&& STRNCMP(p + len, cms2 + 2, STRLEN(cms2 + 2)) == 0) {
@@ -1711,7 +1661,7 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t mark
}
if (u_save(lnum - 1, lnum + 1) == OK) {
// Make new line: text-before-marker + text-after-marker
- newline = xmalloc(STRLEN(line) - len + 1);
+ char_u *newline = xmalloc(STRLEN(line) - len + 1);
assert(p >= line);
memcpy(newline, line, (size_t)(p - line));
STRCPY(newline + (p - line), p + len);
@@ -1737,26 +1687,23 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
{
char_u *text = NULL;
// an error occurred when evaluating 'fdt' setting
- static int got_fdt_error = FALSE;
+ static bool got_fdt_error = false;
int save_did_emsg = did_emsg;
static win_T *last_wp = NULL;
static linenr_T last_lnum = 0;
if (last_wp == NULL || last_wp != wp || last_lnum > lnum || last_lnum == 0) {
// window changed, try evaluating foldtext setting once again
- got_fdt_error = FALSE;
+ got_fdt_error = false;
}
if (!got_fdt_error) {
// a previous error should not abort evaluating 'foldexpr'
- did_emsg = FALSE;
+ did_emsg = false;
}
if (*wp->w_p_fdt != NUL) {
char dashes[MAX_LEVEL + 2];
- win_T *save_curwin;
- int level;
- char_u *p;
// Set "v:foldstart" and "v:foldend".
set_vim_var_nr(VV_FOLDSTART, (varnumber_T)lnum);
@@ -1764,7 +1711,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
// Set "v:folddashes" to a string of "level" dashes.
// Set "v:foldlevel" to "level".
- level = foldinfo.fi_level;
+ int level = foldinfo.fi_level;
if (level > (int)sizeof(dashes) - 1) {
level = (int)sizeof(dashes) - 1;
}
@@ -1775,7 +1722,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
// skip evaluating foldtext on errors
if (!got_fdt_error) {
- save_curwin = curwin;
+ win_T *save_curwin = curwin;
curwin = wp;
curbuf = wp->w_buffer;
@@ -1786,7 +1733,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
emsg_silent--;
if (text == NULL || did_emsg) {
- got_fdt_error = TRUE;
+ got_fdt_error = true;
}
curwin = save_curwin;
@@ -1803,6 +1750,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
if (text != NULL) {
// Replace unprintable characters, if there are any. But
// replace a TAB with a space.
+ char_u *p;
for (p = text; *p != NUL; p++) {
int len = utfc_ptr2len((char *)p);
@@ -1840,11 +1788,6 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
/// Remove 'foldmarker' and 'commentstring' from "str" (in-place).
void foldtext_cleanup(char_u *str)
{
- char_u *s;
- char_u *p;
- bool did1 = false;
- bool did2 = false;
-
// Ignore leading and trailing white space in 'commentstring'.
char_u *cms_start = (char_u *)skipwhite((char *)curbuf->b_p_cms);
size_t cms_slen = STRLEN(cms_start);
@@ -1865,13 +1808,16 @@ void foldtext_cleanup(char_u *str)
}
// skip "%s" and white space after it
- s = (char_u *)skipwhite((char *)cms_end + 2);
+ char_u *s = (char_u *)skipwhite((char *)cms_end + 2);
cms_elen -= (size_t)(s - cms_end);
cms_end = s;
}
parseMarker(curwin);
- for (s = str; *s != NUL;) {
+ bool did1 = false;
+ bool did2 = false;
+
+ for (char_u *s = str; *s != NUL;) {
size_t len = 0;
if (STRNCMP(s, curwin->w_p_fmr, foldstartmarkerlen) == 0) {
len = foldstartmarkerlen;
@@ -1880,11 +1826,12 @@ void foldtext_cleanup(char_u *str)
}
if (len > 0) {
if (ascii_isdigit(s[len])) {
- ++len;
+ len++;
}
// May remove 'commentstring' start. Useful when it's a double
// quote and we already removed a double quote.
+ char_u *p;
for (p = s; p > str && ascii_iswhite(p[-1]); p--) {}
if (p >= str + cms_slen
&& STRNCMP(p - cms_slen, cms_start, cms_slen) == 0) {
@@ -1903,7 +1850,7 @@ void foldtext_cleanup(char_u *str)
}
if (len != 0) {
while (ascii_iswhite(s[len])) {
- ++len;
+ len++;
}
STRMOVE(s, s + len);
} else {
@@ -1920,10 +1867,6 @@ void foldtext_cleanup(char_u *str)
/// IEMS = "Indent Expr Marker Syntax"
static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
{
- fline_T fline;
- LevelGetter getlevel = NULL;
- fold_T *fp;
-
// Avoid problems when being called recursively.
if (invalid_top != (linenr_T)0) {
return;
@@ -1955,6 +1898,8 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
top = wp->w_buffer->b_ml.ml_line_count;
}
+ fline_T fline;
+
fold_changed = false;
fline.wp = wp;
fline.off = 0;
@@ -1967,6 +1912,8 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
invalid_top = top;
invalid_bot = bot;
+ LevelGetter getlevel = NULL;
+
if (foldmethodIsMarker(wp)) {
getlevel = foldlevelMarker;
@@ -2032,13 +1979,11 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
}
}
- /*
- * If folding is defined by the syntax, it is possible that a change in
- * one line will cause all sub-folds of the current fold to change (e.g.,
- * closing a C-style comment can cause folds in the subsequent lines to
- * appear). To take that into account we should adjust the value of "bot"
- * to point to the end of the current fold:
- */
+ // If folding is defined by the syntax, it is possible that a change in
+ // one line will cause all sub-folds of the current fold to change (e.g.,
+ // closing a C-style comment can cause folds in the subsequent lines to
+ // appear). To take that into account we should adjust the value of "bot"
+ // to point to the end of the current fold:
if (foldlevelSyntax == getlevel) {
garray_T *gap = &wp->w_folds;
fold_T *fpn = NULL;
@@ -2050,7 +1995,7 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
if (!foldFind(gap, lnum_rel, &fpn)) {
break;
}
- ++current_fdl;
+ current_fdl++;
fold_start_lnum += fpn->fd_top;
gap = &fpn->fd_nested;
@@ -2071,6 +2016,9 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
if (start > end && end < wp->w_buffer->b_ml.ml_line_count) {
end = start;
}
+
+ fold_T *fp;
+
while (!got_int) {
// Always stop at the end of the file ("end" can be past the end of
// the file).
@@ -2115,7 +2063,7 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
if (fline.lnum == wp->w_buffer->b_ml.ml_line_count) {
break;
}
- ++fline.lnum;
+ fline.lnum++;
fline.lvl = fline.lvl_next;
getlevel(&fline);
}
@@ -2173,23 +2121,12 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
const linenr_T startlnum, fline_T *const flp,
LevelGetter getlevel, linenr_T bot, const char topflags)
{
- linenr_T ll;
fold_T *fp = NULL;
- fold_T *fp2;
- int lvl = level;
- linenr_T startlnum2 = startlnum;
- const linenr_T firstlnum = flp->lnum; // first lnum we got
- int i;
- bool finish = false;
- const linenr_T linecount = flp->wp->w_buffer->b_ml.ml_line_count - flp->off;
- int concat;
-
- /*
- * If using the marker method, the start line is not the start of a fold
- * at the level we're dealing with and the level is non-zero, we must use
- * the previous fold. But ignore a fold that starts at or below
- * startlnum, it must be deleted.
- */
+
+ // If using the marker method, the start line is not the start of a fold
+ // at the level we're dealing with and the level is non-zero, we must use
+ // the previous fold. But ignore a fold that starts at or below
+ // startlnum, it must be deleted.
if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level
&& flp->lvl > 0) {
(void)foldFind(gap, startlnum - 1, &fp);
@@ -2200,17 +2137,22 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
}
}
- /*
- * Loop over all lines in this fold, or until "bot" is hit.
- * Handle nested folds inside of this fold.
- * "flp->lnum" is the current line. When finding the end of the fold, it
- * is just below the end of the fold.
- * "*flp" contains the level of the line "flp->lnum" or a following one if
- * there are lines with an invalid fold level. "flp->lnum_save" is the
- * line number that was used to get the fold level (below "flp->lnum" when
- * it has an invalid fold level). When called the fold level is always
- * valid, thus "flp->lnum_save" is equal to "flp->lnum".
- */
+ fold_T *fp2;
+ int lvl = level;
+ linenr_T startlnum2 = startlnum;
+ const linenr_T firstlnum = flp->lnum; // first lnum we got
+ bool finish = false;
+ const linenr_T linecount = flp->wp->w_buffer->b_ml.ml_line_count - flp->off;
+
+ // Loop over all lines in this fold, or until "bot" is hit.
+ // Handle nested folds inside of this fold.
+ // "flp->lnum" is the current line. When finding the end of the fold, it
+ // is just below the end of the fold.
+ // "*flp" contains the level of the line "flp->lnum" or a following one if
+ // there are lines with an invalid fold level. "flp->lnum_save" is the
+ // line number that was used to get the fold level (below "flp->lnum" when
+ // it has an invalid fold level). When called the fold level is always
+ // valid, thus "flp->lnum_save" is equal to "flp->lnum".
flp->lnum_save = flp->lnum;
while (!got_int) {
// Updating folds can be slow, check for CTRL-C.
@@ -2240,15 +2182,15 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
&& getlevel != foldlevelSyntax) {
break;
}
- i = 0;
+ int i = 0;
fp2 = fp;
if (lvl >= level) {
// Compute how deep the folds currently are, if it's deeper
// than "lvl" then some must be deleted, need to update
// at least one nested fold.
- ll = flp->lnum - fp->fd_top;
+ int ll = flp->lnum - fp->fd_top;
while (foldFind(&fp2->fd_nested, ll, &fp2)) {
- ++i;
+ i++;
ll -= fp2->fd_top;
}
}
@@ -2273,13 +2215,12 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
|| flp->start != 0
|| flp->had_end <= MAX_LEVEL
|| flp->lnum == linecount)) {
- /*
- * Remove or update folds that have lines between startlnum and
- * firstlnum.
- */
+ // Remove or update folds that have lines between startlnum and
+ // firstlnum.
while (!got_int) {
// set concat to 1 if it's allowed to concatenate this fold
// with a previous one that touches it.
+ int concat;
if (flp->start != 0 || flp->had_end <= MAX_LEVEL) {
concat = 0;
} else {
@@ -2347,7 +2288,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
}
foldRemove(flp->wp, &fp->fd_nested, breakstart - fp->fd_top,
breakend - fp->fd_top);
- i = (int)(fp - (fold_T *)gap->ga_data);
+ int i = (int)(fp - (fold_T *)gap->ga_data);
foldSplit(flp->wp->w_buffer, gap, i, breakstart, breakend - 1);
fp = (fold_T *)gap->ga_data + i + 1;
// If using the "marker" or "syntax" method, we
@@ -2360,7 +2301,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
}
}
if (fp->fd_top == startlnum && concat) {
- i = (int)(fp - (fold_T *)gap->ga_data);
+ int i = (int)(fp - (fold_T *)gap->ga_data);
if (i != 0) {
fp2 = fp - 1;
if (fp2->fd_top + fp2->fd_len == fp->fd_top) {
@@ -2389,6 +2330,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
} else {
// Insert new fold. Careful: ga_data may be NULL and it
// may change!
+ int i;
if (gap->ga_len == 0) {
i = 0;
} else {
@@ -2429,17 +2371,13 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
}
if (lvl < level || flp->lnum > linecount) {
- /*
- * Found a line with a lower foldlevel, this fold ends just above
- * "flp->lnum".
- */
+ // Found a line with a lower foldlevel, this fold ends just above
+ // "flp->lnum".
break;
}
- /*
- * The fold includes the line "flp->lnum" and "flp->lnum_save".
- * Check "fp" for safety.
- */
+ // The fold includes the line "flp->lnum" and "flp->lnum_save".
+ // Check "fp" for safety.
if (lvl > level && fp != NULL) {
// There is a nested fold, handle it recursively.
// At least do one line (can happen when finish is true).
@@ -2451,7 +2389,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
// this fold.
flp->lnum = flp->lnum_save - fp->fd_top;
flp->off += fp->fd_top;
- i = (int)(fp - (fold_T *)gap->ga_data);
+ int i = (int)(fp - (fold_T *)gap->ga_data);
bot = foldUpdateIEMSRecurse(&fp->fd_nested, level + 1,
startlnum2 - fp->fd_top, flp, getlevel,
bot - fp->fd_top, fp->fd_flags);
@@ -2464,14 +2402,12 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
// This fold may end at the same line, don't incr. flp->lnum.
} else {
- /*
- * Get the level of the next line, then continue the loop to check
- * if it ends there.
- * Skip over undefined lines, to find the foldlevel after it.
- * For the last line in the file the foldlevel is always valid.
- */
+ // Get the level of the next line, then continue the loop to check
+ // if it ends there.
+ // Skip over undefined lines, to find the foldlevel after it.
+ // For the last line in the file the foldlevel is always valid.
flp->lnum = flp->lnum_save;
- ll = flp->lnum + 1;
+ int ll = flp->lnum + 1;
while (!got_int) {
// Make the previous level available to foldlevel().
prev_lnum = flp->lnum;
@@ -2502,11 +2438,9 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
return bot;
}
- /*
- * Get here when:
- * lvl < level: the folds ends just above "flp->lnum"
- * lvl >= level: fold continues below "bot"
- */
+ // Get here when:
+ // lvl < level: the folds ends just above "flp->lnum"
+ // lvl >= level: fold continues below "bot"
// Current fold at least extends until lnum.
if (fp->fd_len < flp->lnum - fp->fd_top) {
@@ -2538,7 +2472,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
} else {
// indent or expr method: split fold to create a new one
// below bot
- i = (int)(fp - (fold_T *)gap->ga_data);
+ int i = (int)(fp - (fold_T *)gap->ga_data);
foldSplit(flp->wp->w_buffer, gap, i, flp->lnum, bot);
fp = (fold_T *)gap->ga_data + i;
}
@@ -2590,11 +2524,9 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
/// Insert a new fold in "gap" at position "i".
static void foldInsert(garray_T *gap, int i)
{
- fold_T *fp;
-
ga_grow(gap, 1);
- fp = (fold_T *)gap->ga_data + i;
+ fold_T *fp = (fold_T *)gap->ga_data + i;
if (gap->ga_len > 0 && i < gap->ga_len) {
memmove(fp + 1, fp, sizeof(fold_T) * (size_t)(gap->ga_len - i));
}
@@ -2666,12 +2598,12 @@ static void foldSplit(buf_T *buf, garray_T *const gap, const int i, const linenr
/// 6: not changed
static void foldRemove(win_T *const wp, garray_T *gap, linenr_T top, linenr_T bot)
{
- fold_T *fp = NULL;
-
if (bot < top) {
return; // nothing to do
}
+ fold_T *fp = NULL;
+
while (gap->ga_len > 0) {
// Find fold that includes top or a following one.
if (foldFind(gap, top, &fp) && fp->fd_top < top) {
@@ -2881,7 +2813,6 @@ static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2)
{
fold_T *fp3;
fold_T *fp4;
- int idx;
garray_T *gap1 = &fp1->fd_nested;
garray_T *gap2 = &fp2->fd_nested;
@@ -2894,11 +2825,11 @@ static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2)
// Move nested folds in fp2 to the end of fp1.
if (!GA_EMPTY(gap2)) {
ga_grow(gap1, gap2->ga_len);
- for (idx = 0; idx < gap2->ga_len; ++idx) {
+ for (int idx = 0; idx < gap2->ga_len; idx++) {
((fold_T *)gap1->ga_data)[gap1->ga_len]
= ((fold_T *)gap2->ga_data)[idx];
((fold_T *)gap1->ga_data)[gap1->ga_len].fd_top += fp1->fd_len;
- ++gap1->ga_len;
+ gap1->ga_len++;
}
gap2->ga_len = 0;
}
@@ -2915,12 +2846,10 @@ static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2)
/// @return a level of -1 if the foldlevel depends on surrounding lines.
static void foldlevelIndent(fline_T *flp)
{
- char_u *s;
- buf_T *buf;
linenr_T lnum = flp->lnum + flp->off;
- buf = flp->wp->w_buffer;
- s = (char_u *)skipwhite((char *)ml_get_buf(buf, lnum, false));
+ buf_T *buf = flp->wp->w_buffer;
+ char_u *s = (char_u *)skipwhite((char *)ml_get_buf(buf, lnum, false));
// empty line or lines starting with a character in 'foldignore': level
// depends on surrounding lines
@@ -2958,11 +2887,9 @@ static void foldlevelDiff(fline_T *flp)
/// @return a level of -1 if the foldlevel depends on surrounding lines.
static void foldlevelExpr(fline_T *flp)
{
- win_T *win;
- int c;
linenr_T lnum = flp->lnum + flp->off;
- win = curwin;
+ win_T *win = curwin;
curwin = flp->wp;
curbuf = flp->wp->w_buffer;
set_vim_var_nr(VV_LNUM, (varnumber_T)lnum);
@@ -2977,6 +2904,8 @@ static void foldlevelExpr(fline_T *flp)
// KeyTyped may be reset to 0 when calling a function which invokes
// do_cmdline(). To make 'foldopen' work correctly restore KeyTyped.
const bool save_keytyped = KeyTyped;
+
+ int c;
const int n = eval_foldexpr((char *)flp->wp->w_p_fde, &c);
KeyTyped = save_keytyped;
@@ -3070,31 +2999,26 @@ static void parseMarker(win_T *wp)
/// Sets flp->start when a start marker was found.
static void foldlevelMarker(fline_T *flp)
{
- char_u *startmarker;
- int cstart;
- int cend;
int start_lvl = flp->lvl;
- char_u *s;
- int n;
// cache a few values for speed
- startmarker = flp->wp->w_p_fmr;
- cstart = *startmarker;
- ++startmarker;
- cend = *foldendmarker;
+ char_u *startmarker = flp->wp->w_p_fmr;
+ int cstart = *startmarker;
+ startmarker++;
+ int cend = *foldendmarker;
// Default: no start found, next level is same as current level
flp->start = 0;
flp->lvl_next = flp->lvl;
- s = ml_get_buf(flp->wp->w_buffer, flp->lnum + flp->off, false);
+ char_u *s = ml_get_buf(flp->wp->w_buffer, flp->lnum + flp->off, false);
while (*s) {
if (*s == cstart
&& STRNCMP(s + 1, startmarker, foldstartmarkerlen - 1) == 0) {
// found startmarker: set flp->lvl
s += foldstartmarkerlen;
if (ascii_isdigit(*s)) {
- n = atoi((char *)s);
+ int n = atoi((char *)s);
if (n > 0) {
flp->lvl = n;
flp->lvl_next = n;
@@ -3105,16 +3029,16 @@ static void foldlevelMarker(fline_T *flp)
}
}
} else {
- ++flp->lvl;
- ++flp->lvl_next;
- ++flp->start;
+ flp->lvl++;
+ flp->lvl_next++;
+ flp->start++;
}
} else if (*s == cend && STRNCMP(s + 1, foldendmarker + 1,
foldendmarkerlen - 1) == 0) {
// found endmarker: set flp->lvl_next
s += foldendmarkerlen;
if (ascii_isdigit(*s)) {
- n = atoi((char *)s);
+ int n = atoi((char *)s);
if (n > 0) {
flp->lvl = n;
flp->lvl_next = n - 1;
@@ -3143,13 +3067,12 @@ static void foldlevelMarker(fline_T *flp)
static void foldlevelSyntax(fline_T *flp)
{
linenr_T lnum = flp->lnum + flp->off;
- int n;
// Use the maximum fold level at the start of this line and the next.
flp->lvl = syn_get_foldlevel(flp->wp, lnum);
flp->start = 0;
if (lnum < flp->wp->w_buffer->b_ml.ml_line_count) {
- n = syn_get_foldlevel(flp->wp, lnum + 1);
+ int n = syn_get_foldlevel(flp->wp, lnum + 1);
if (n > flp->lvl) {
flp->start = n - flp->lvl; // fold(s) start here
flp->lvl = n;
@@ -3198,7 +3121,7 @@ static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off)
|| put_eol(fd) == FAIL) {
return FAIL;
}
- ++fp;
+ fp++;
}
return OK;
}
@@ -3209,8 +3132,6 @@ static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off)
/// @return FAIL when writing failed.
static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off)
{
- int level;
-
fold_T *fp = (fold_T *)gap->ga_data;
for (int i = 0; i < gap->ga_len; i++) {
if (fp->fd_flags != FD_LEVEL) {
@@ -3236,7 +3157,7 @@ static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off
// Open or close the leaf according to the window foldlevel.
// Do not close a leaf that is already closed, as it will close
// the parent.
- level = foldLevelWin(wp, off + fp->fd_top);
+ int level = foldLevelWin(wp, off + fp->fd_top);
if ((fp->fd_flags == FD_CLOSED && wp->w_p_fdl >= level)
|| (fp->fd_flags != FD_CLOSED && wp->w_p_fdl < level)) {
if (put_fold_open_close(fd, fp, off) == FAIL) {
@@ -3245,7 +3166,7 @@ static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off
}
}
}
- ++fp;
+ fp++;
}
return OK;
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index 0f7052d351..4cf282770d 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -292,7 +292,7 @@ for i = 1, #functions do
if fn.check_textlock then
output:write('\n if (textlock != 0) {')
- output:write('\n api_set_error(error, kErrorTypeException, "%s", e_secure);')
+ output:write('\n api_set_error(error, kErrorTypeException, "%s", e_textlock);')
output:write('\n goto cleanup;')
output:write('\n }\n')
end
@@ -435,7 +435,7 @@ local function process_function(fn)
if fn.check_textlock then
write_shifted_output(output, [[
if (textlock != 0) {
- api_set_error(&err, kErrorTypeException, "%s", e_secure);
+ api_set_error(&err, kErrorTypeException, "%s", e_textlock);
goto exit_0;
}
]])
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 6fdc10ae0b..00372d4f3d 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1545,12 +1545,16 @@ int vgetc(void)
}
break;
+ case K_KUP:
case K_XUP:
c = K_UP; break;
+ case K_KDOWN:
case K_XDOWN:
c = K_DOWN; break;
+ case K_KLEFT:
case K_XLEFT:
c = K_LEFT; break;
+ case K_KRIGHT:
case K_XRIGHT:
c = K_RIGHT; break;
}
@@ -1804,7 +1808,8 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
bool is_plug_map = false;
// If typehead starts with <Plug> then remap, even for a "noremap" mapping.
- if (typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL
+ if (typebuf.tb_len >= 3
+ && typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL
&& typebuf.tb_buf[typebuf.tb_off + 1] == KS_EXTRA
&& typebuf.tb_buf[typebuf.tb_off + 2] == KE_PLUG) {
is_plug_map = true;
@@ -2470,6 +2475,11 @@ static int vgetorpeek(bool advance)
}
tc = c;
+ // return 0 in normal_check()
+ if (pending_exmode_active) {
+ exmode_active = true;
+ }
+
// no chars to block abbreviations for
typebuf.tb_no_abbr_cnt = 0;
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index a2862edd6b..8d896aef31 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -224,12 +224,12 @@ EXTERN dict_T vimvardict; // Dictionary with v: variables
EXTERN dict_T globvardict; // Dictionary with g: variables
/// g: value
#define globvarht globvardict.dv_hashtab
-EXTERN int did_emsg; // set by emsg() when the message
+EXTERN bool did_emsg; // set by emsg() when the message
// is displayed or thrown
EXTERN bool called_vim_beep; // set if vim_beep() is called
EXTERN bool did_emsg_syntax; // did_emsg set because of a
// syntax error
-EXTERN int called_emsg; // always set by emsg()
+EXTERN int called_emsg; // always incremented by emsg()
EXTERN int ex_exitval INIT(= 0); // exit value for ex mode
EXTERN bool emsg_on_display INIT(= false); // there is an error message
EXTERN bool rc_did_emsg INIT(= false); // vim_regcomp() called emsg()
@@ -274,11 +274,11 @@ EXTERN except_T *current_exception;
/// Set when a throw that cannot be handled in do_cmdline() must be propagated
/// to the cstack of the previously called do_cmdline().
-EXTERN int need_rethrow INIT(= false);
+EXTERN bool need_rethrow INIT(= false);
/// Set when a ":finish" or ":return" that cannot be handled in do_cmdline()
/// must be propagated to the cstack of the previously called do_cmdline().
-EXTERN int check_cstack INIT(= false);
+EXTERN bool check_cstack INIT(= false);
/// Number of nested try conditionals (across function calls and ":source"
/// commands).
@@ -503,14 +503,13 @@ EXTERN int stdout_isatty INIT(= true);
EXTERN int stdin_fd INIT(= -1);
// true when doing full-screen output, otherwise only writing some messages.
-// volatile because it is used in a signal handler.
-EXTERN volatile int full_screen INIT(= false);
+EXTERN int full_screen INIT(= false);
/// Non-zero when only "safe" commands are allowed, e.g. when sourcing .exrc or
/// .vimrc in current directory.
EXTERN int secure INIT(= 0);
-/// Non-zero when changing text and jumping to another window/buffer is not
+/// Non-zero when changing text and jumping to another window or editing another buffer is not
/// allowed.
EXTERN int textlock INIT(= 0);
@@ -637,6 +636,10 @@ EXTERN int motion_force INIT(=0); // motion force for pending operator
// Ex Mode (Q) state
EXTERN bool exmode_active INIT(= false); // true if Ex mode is active
+
+/// Flag set when normal_check() should return 0 when entering Ex mode.
+EXTERN bool pending_exmode_active INIT(= false);
+
EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p.
// 'inccommand' command preview state
@@ -724,9 +727,9 @@ EXTERN bool need_highlight_changed INIT(= true);
EXTERN FILE *scriptout INIT(= NULL); ///< Stream to write script to.
-// volatile because it is used in a signal handler.
-EXTERN volatile int got_int INIT(= false); // set to true when interrupt
- // signal occurred
+// Note that even when handling SIGINT, volatile is not necessary because the
+// callback is not called directly from the signal handlers.
+EXTERN bool got_int INIT(= false); // set to true when interrupt signal occurred
EXTERN bool bangredo INIT(= false); // set to true with ! command
EXTERN int searchcmdlen; // length of previous search cmd
EXTERN int reg_do_extmatch INIT(= 0); // Used when compiling regexp:
@@ -956,6 +959,7 @@ EXTERN char e_listdictblobarg[] INIT(= N_("E896: Argument of %s must be a List,
EXTERN char e_readerrf[] INIT(= N_("E47: Error while reading errorfile"));
EXTERN char e_sandbox[] INIT(= N_("E48: Not allowed in sandbox"));
EXTERN char e_secure[] INIT(= N_("E523: Not allowed here"));
+EXTERN char e_textlock[] INIT(= N_("E565: Not allowed to change text or change window"));
EXTERN char e_screenmode[] INIT(= N_("E359: Screen mode setting not supported"));
EXTERN char e_scroll[] INIT(= N_("E49: Invalid scroll size"));
EXTERN char e_shellempty[] INIT(= N_("E91: 'shell' option is empty"));
@@ -986,6 +990,7 @@ EXTERN char e_notset[] INIT(= N_("E764: Option '%s' is not set"));
EXTERN char e_invalidreg[] INIT(= N_("E850: Invalid register name"));
EXTERN char e_dirnotf[] INIT(= N_("E919: Directory not found in '%s': \"%s\""));
EXTERN char e_au_recursive[] INIT(= N_("E952: Autocommand caused recursive behavior"));
+EXTERN char e_menuothermode[] INIT(= N_("E328: Menu only exists in another mode"));
EXTERN char e_autocmd_close[] INIT(= N_("E813: Cannot close autocmd window"));
EXTERN char e_listarg[] INIT(= N_("E686: Argument of %s must be a List"));
EXTERN char e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
@@ -1075,4 +1080,6 @@ typedef enum {
// Only filled for Win32.
EXTERN char windowsVersion[20] INIT(= { 0 });
+EXTERN int exit_need_delay INIT(= 0);
+
#endif // NVIM_GLOBALS_H
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index 7d407bd3d1..1268f987e1 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -241,7 +241,7 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
u8c = utfc_ptr2char(ptr, u8cc);
}
mbyte_cells = utf_char2cells(u8c);
- if (p_arshape && !p_tbidi && arabic_char(u8c)) {
+ if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
// Do Arabic shaping.
if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) {
// Past end of string to be displayed.
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index b96220d547..a4cf65e816 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -342,7 +342,7 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_
break;
}
- table[idx].number = getdigits_int(&p, false, 0);
+ table[idx].number = getdigits_int((char **)&p, false, 0);
}
table[idx].string = p;
@@ -659,7 +659,8 @@ void ex_hardcopy(exarg_T *eap)
*/
if (mch_print_init(&settings,
curbuf->b_fname == NULL ? (char_u *)buf_spname(curbuf) : curbuf->b_sfname ==
- NULL ? (char_u *)curbuf->b_fname : curbuf->b_sfname, eap->forceit) == FAIL) {
+ NULL ? (char_u *)curbuf->b_fname : (char_u *)curbuf->b_sfname,
+ eap->forceit) == FAIL) {
return;
}
diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c
index ca6f033c47..95ae7a152c 100644
--- a/src/nvim/hashtab.c
+++ b/src/nvim/hashtab.c
@@ -85,9 +85,9 @@ void hash_clear_all(hashtab_T *ht, unsigned int off)
/// used for that key.
/// WARNING: Returned pointer becomes invalid as soon as the hash table
/// is changed in any way.
-hashitem_T *hash_find(const hashtab_T *const ht, const char_u *const key)
+hashitem_T *hash_find(const hashtab_T *const ht, const char *const key)
{
- return hash_lookup(ht, (const char *)key, STRLEN(key), hash_hash(key));
+ return hash_lookup(ht, key, STRLEN(key), hash_hash((char_u *)key));
}
/// Like hash_find, but key is not NUL-terminated
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 8a5b4cbf0f..271498d41a 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -400,7 +400,7 @@ int get_number_indent(linenr_T lnum)
// In format_lines() (i.e. not insert mode), fo+=q is needed too...
if ((State & MODE_INSERT) || has_format_option(FO_Q_COMS)) {
- lead_len = get_leader_len(ml_get(lnum), NULL, false, true);
+ lead_len = get_leader_len((char *)ml_get(lnum), NULL, false, true);
}
regmatch.regprog = vim_regcomp((char *)curbuf->b_p_flp, RE_MAGIC);
@@ -409,7 +409,7 @@ int get_number_indent(linenr_T lnum)
// vim_regexec() expects a pointer to a line. This lets us
// start matching for the flp beyond any comment leader...
- if (vim_regexec(&regmatch, ml_get(lnum) + lead_len, (colnr_T)0)) {
+ if (vim_regexec(&regmatch, (char *)ml_get(lnum) + lead_len, (colnr_T)0)) {
pos.lnum = lnum;
pos.col = (colnr_T)(*regmatch.endp - ml_get(lnum));
pos.coladd = 0;
@@ -468,7 +468,7 @@ int get_breakindent_win(win_T *wp, char_u *line)
if (regmatch.regprog != NULL) {
regmatch.rm_ic = false;
- if (vim_regexec(&regmatch, line, 0)) {
+ if (vim_regexec(&regmatch, (char *)line, 0)) {
if (wp->w_briopt_list > 0) {
bri += wp->w_briopt_list;
} else {
@@ -656,6 +656,9 @@ int get_lisp_indent(void)
}
}
}
+ if (*that == NUL) {
+ break;
+ }
}
if ((*that == '(') || (*that == '[')) {
parencount++;
@@ -769,7 +772,7 @@ static int lisp_match(char_u *p)
char_u *word = *curbuf->b_p_lw != NUL ? curbuf->b_p_lw : p_lispwords;
while (*word != NUL) {
- (void)copy_option_part(&word, buf, LSIZE, ",");
+ (void)copy_option_part((char **)&word, (char *)buf, LSIZE, ",");
len = (int)STRLEN(buf);
if ((STRNCMP(buf, p, len) == 0) && (p[len] == ' ')) {
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index cb807dec24..3c74b4bd8d 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -224,7 +224,7 @@ bool cin_is_cinword(const char_u *line)
line = (char_u *)skipwhite((char *)line);
for (char_u *cinw = curbuf->b_p_cinw; *cinw;) {
- size_t len = copy_option_part(&cinw, cinw_buf, cinw_len, ",");
+ size_t len = copy_option_part((char **)&cinw, (char *)cinw_buf, cinw_len, ",");
if (STRNCMP(line, cinw_buf, len) == 0
&& (!vim_iswordc(line[len]) || !vim_iswordc(line[len - 1]))) {
retval = true;
@@ -520,7 +520,7 @@ bool cin_isscopedecl(const char_u *p)
bool found = false;
for (char_u *cinsd = curbuf->b_p_cinsd; *cinsd;) {
- const size_t len = copy_option_part(&cinsd, cinsd_buf, cinsd_len, ",");
+ const size_t len = copy_option_part((char **)&cinsd, (char *)cinsd_buf, cinsd_len, ",");
if (STRNCMP(s, cinsd_buf, len) == 0) {
const char_u *skip = cin_skipcomment(s + len);
if (*skip == ':' && skip[1] != ':') {
@@ -1501,7 +1501,7 @@ retry:
if ((trypos = findmatchlimit(NULL, c, 0, ind_maxp_wk)) != NULL) {
// check if the ( is in a // comment
if ((colnr_T)cin_skip2pos(trypos) > trypos->col) {
- ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum - trypos->lnum);
+ ind_maxp_wk = ind_maxparen - (cursor_save.lnum - trypos->lnum);
if (ind_maxp_wk > 0) {
curwin->w_cursor = *trypos;
curwin->w_cursor.col = 0; // XXX
@@ -1515,7 +1515,7 @@ retry:
trypos = &pos_copy;
curwin->w_cursor = *trypos;
if ((trypos_wk = ind_find_start_CORS(NULL)) != NULL) { // XXX
- ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum - trypos_wk->lnum);
+ ind_maxp_wk = ind_maxparen - (cursor_save.lnum - trypos_wk->lnum);
if (ind_maxp_wk > 0) {
curwin->w_cursor = *trypos_wk;
goto retry;
@@ -1746,7 +1746,7 @@ void parse_cino(buf_T *buf)
p++;
}
char_u *digits_start = p; // remember where the digits start
- int n = getdigits_int(&p, true, 0);
+ int n = getdigits_int((char **)&p, true, 0);
divider = 0;
if (*p == '.') { // ".5s" means a fraction.
fraction = atoi((char *)++p);
@@ -2075,7 +2075,7 @@ int get_c_indent(void)
} else if (*p == COM_LEFT || *p == COM_RIGHT) {
align = *p++;
} else if (ascii_isdigit(*p) || *p == '-') {
- off = getdigits_int(&p, true, 0);
+ off = getdigits_int((char **)&p, true, 0);
} else {
p++;
}
@@ -2084,7 +2084,7 @@ int get_c_indent(void)
if (*p == ':') {
p++;
}
- (void)copy_option_part(&p, (char_u *)lead_end, COM_MAX_LEN, ",");
+ (void)copy_option_part((char **)&p, lead_end, COM_MAX_LEN, ",");
if (what == COM_START) {
STRCPY(lead_start, lead_end);
lead_start_len = (int)STRLEN(lead_start);
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 164542f4ab..ad03ebd1ed 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -36,6 +36,7 @@
#include "nvim/message.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/os.h"
+#include "nvim/profile.h"
#include "nvim/screen.h"
#include "nvim/undo.h"
#include "nvim/version.h"
@@ -47,6 +48,8 @@ static int in_fast_callback = 0;
// Initialized in nlua_init().
static lua_State *global_lstate = NULL;
+static LuaRef require_ref = LUA_REFNIL;
+
static uv_thread_t main_thread;
typedef struct {
@@ -645,6 +648,16 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
nlua_common_vim_init(lstate, false);
+ // patch require() (only for --startuptime)
+ if (time_fd != NULL) {
+ lua_getglobal(lstate, "require");
+ // Must do this after nlua_common_vim_init where nlua_global_refs is initialized.
+ require_ref = nlua_ref_global(lstate, -1);
+ lua_pop(lstate, 1);
+ lua_pushcfunction(lstate, &nlua_require);
+ lua_setglobal(lstate, "require");
+ }
+
// internal vim._treesitter... API
nlua_add_treesitter(lstate);
@@ -740,6 +753,7 @@ void nlua_free_all_mem(void)
return;
}
lua_State *lstate = global_lstate;
+ nlua_unref_global(lstate, require_ref);
nlua_common_free_all_mem(lstate);
}
@@ -870,6 +884,62 @@ nlua_print_error:
return lua_error(lstate);
}
+/// require() for --startuptime
+///
+/// @param lstate Lua interpreter state.
+static int nlua_require(lua_State *const lstate)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const char *name = luaL_checkstring(lstate, 1);
+ lua_settop(lstate, 1);
+ // [ name ]
+
+ // try cached module from package.loaded first
+ lua_getfield(lstate, LUA_REGISTRYINDEX, "_LOADED");
+ lua_getfield(lstate, 2, name);
+ // [ name package.loaded module ]
+ if (lua_toboolean(lstate, -1)) {
+ return 1;
+ }
+ lua_pop(lstate, 2);
+ // [ name ]
+
+ // push original require below the module name
+ nlua_pushref(lstate, require_ref);
+ lua_insert(lstate, 1);
+ // [ require name ]
+
+ if (time_fd == NULL) {
+ // after log file was closed, try to restore
+ // global require to the original function...
+ lua_getglobal(lstate, "require");
+ // ...only if it's still referencing this wrapper,
+ // to not overwrite it in case someone happened to
+ // patch it in the meantime...
+ if (lua_iscfunction(lstate, -1) && lua_tocfunction(lstate, -1) == nlua_require) {
+ lua_pushvalue(lstate, 1);
+ lua_setglobal(lstate, "require");
+ }
+ lua_pop(lstate, 1);
+
+ // ...and then call require directly.
+ lua_call(lstate, 1, 1);
+ return 1;
+ }
+
+ proftime_T rel_time;
+ proftime_T start_time;
+ time_push(&rel_time, &start_time);
+ int status = lua_pcall(lstate, 1, 1, 0);
+ if (status == 0) {
+ vim_snprintf((char *)IObuff, IOSIZE, "require('%s')", name);
+ time_msg((char *)IObuff, &start_time);
+ }
+ time_pop(rel_time);
+
+ return status == 0 ? 1 : lua_error(lstate);
+}
+
/// debug.debug: interaction with user while debugging.
///
/// @param lstate Lua interpreter state.
@@ -1915,7 +1985,7 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
// every possible modifier (with room to spare). If the list of possible
// modifiers grows this may need to be updated.
char buf[200] = { 0 };
- (void)uc_mods(buf);
+ (void)uc_mods(buf, &cmdmod, false);
lua_pushstring(lstate, buf);
lua_setfield(lstate, -2, "mods");
@@ -1946,6 +2016,8 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
lua_setfield(lstate, -2, "silent");
lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_ERRSILENT);
lua_setfield(lstate, -2, "emsg_silent");
+ lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_UNSILENT);
+ lua_setfield(lstate, -2, "unsilent");
lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_SANDBOX);
lua_setfield(lstate, -2, "sandbox");
lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_NOAUTOCMD);
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index b911eb8b59..8fde85b163 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -55,7 +55,7 @@ static int regex_match(lua_State *lstate, regprog_T **prog, char_u *str)
regmatch_T rm;
rm.regprog = *prog;
rm.rm_ic = false;
- bool match = vim_regexec(&rm, str, 0);
+ bool match = vim_regexec(&rm, (char *)str, 0);
*prog = rm.regprog;
if (match) {
@@ -252,7 +252,7 @@ static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
if (offset < 0 || offset > (intptr_t)s1_len) {
return luaL_error(lstate, "index out of range");
}
- int tail_offset = mb_tail_off((char_u *)s1, (char_u *)s1 + offset - 1);
+ int tail_offset = mb_tail_off(s1, s1 + offset - 1);
lua_pushinteger(lstate, tail_offset);
return 1;
}
@@ -300,7 +300,9 @@ int nlua_regex(lua_State *lstate)
});
if (ERROR_SET(&err)) {
- return luaL_error(lstate, "couldn't parse regex: %s", err.msg);
+ nlua_push_errstr(lstate, "couldn't parse regex: %s", err.msg);
+ api_clear_error(&err);
+ return lua_error(lstate);
}
assert(prog);
@@ -338,12 +340,14 @@ static dict_T *nlua_get_var_scope(lua_State *lstate)
dict = tabpage->tp_vars;
}
} else {
- luaL_error(lstate, "invalid scope", err.msg);
+ luaL_error(lstate, "invalid scope");
return NULL;
}
if (ERROR_SET(&err)) {
- luaL_error(lstate, "FAIL: %s", err.msg);
+ nlua_push_errstr(lstate, "scoped variable: %s", err.msg);
+ api_clear_error(&err);
+ lua_error(lstate);
return NULL;
}
return dict;
@@ -537,3 +541,14 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
lua_cjson_new(lstate);
lua_setfield(lstate, -2, "json");
}
+
+/// like luaL_error, but allow cleanup
+void nlua_push_errstr(lua_State *L, const char *fmt, ...)
+{
+ va_list argp;
+ va_start(argp, fmt);
+ luaL_where(L, 1);
+ lua_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ lua_concat(L, 2);
+}
diff --git a/src/nvim/main.c b/src/nvim/main.c
index a7e39b7655..b06b9630e2 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -356,7 +356,14 @@ int main(int argc, char **argv)
abort(); // unreachable
}
- init_default_mappings(); // Default mappings.
+ // Default mappings (incl. menus)
+ Error err = ERROR_INIT;
+ Object o = nlua_exec(STATIC_CSTR_AS_STRING("return vim._init_default_mappings()"),
+ (Array)ARRAY_DICT_INIT, &err);
+ assert(!ERROR_SET(&err));
+ api_clear_error(&err);
+ assert(o.type == kObjectTypeNil);
+ api_free_object(o);
TIME_MSG("init default mappings");
init_default_autocmds();
@@ -802,7 +809,7 @@ static void init_locale(void)
char localepath[MAXPATHL] = { 0 };
snprintf(localepath, sizeof(localepath), "%s", get_vim_var_str(VV_PROGPATH));
- char *tail = (char *)path_tail_with_sep((char_u *)localepath);
+ char *tail = path_tail_with_sep(localepath);
*tail = NUL;
tail = path_tail(localepath);
xstrlcpy(tail, "share/locale",
@@ -1354,11 +1361,11 @@ scripterror:
// Add the file to the global argument list.
ga_grow(&global_alist.al_ga, 1);
- char_u *p = vim_strsave((char_u *)argv[0]);
+ char *p = xstrdup(argv[0]);
- if (parmp->diff_mode && os_isdir(p) && GARGCOUNT > 0
+ if (parmp->diff_mode && os_isdir((char_u *)p) && GARGCOUNT > 0
&& !os_isdir((char_u *)alist_name(&GARGLIST[0]))) {
- char_u *r = (char_u *)concat_fnames((char *)p, path_tail(alist_name(&GARGLIST[0])), true);
+ char *r = concat_fnames(p, path_tail(alist_name(&GARGLIST[0])), true);
xfree(p);
p = r;
}
@@ -1371,7 +1378,7 @@ scripterror:
int alist_fnum_flag = edit_stdin(had_stdin_file, parmp)
? 1 // add buffer nr after exp.
: 2; // add buffer number now and use curbuf
- alist_add(&global_alist, (char *)p, alist_fnum_flag);
+ alist_add(&global_alist, p, alist_fnum_flag);
}
// If there are no more letters after the current "-", go to next argument.
diff --git a/src/nvim/map.c b/src/nvim/map.c
index d27e40b4ee..d3058a5d52 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -30,6 +30,8 @@
#define int_eq kh_int_hash_equal
#define handle_T_hash kh_int_hash_func
#define handle_T_eq kh_int_hash_equal
+#define KittyKey_hash kh_int_hash_func
+#define KittyKey_eq kh_int_hash_equal
#if defined(ARCH_64)
# define ptr_t_hash(key) uint64_t_hash((uint64_t)(key))
@@ -162,6 +164,7 @@ static inline bool ColorKey_eq(ColorKey ae1, ColorKey ae2)
}
MAP_IMPL(int, int, DEFAULT_INITIALIZER)
+MAP_IMPL(int, cstr_t, DEFAULT_INITIALIZER)
MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(cstr_t, int, DEFAULT_INITIALIZER)
MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER)
@@ -177,6 +180,8 @@ MAP_IMPL(int, String, DEFAULT_INITIALIZER)
MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER)
+MAP_IMPL(KittyKey, cstr_t, DEFAULT_INITIALIZER)
+
/// Deletes a key:value pair from a string:pointer map, and frees the
/// storage of both key and value.
///
diff --git a/src/nvim/map.h b/src/nvim/map.h
index 4f4aaa3552..845daac3f7 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -7,6 +7,7 @@
#include "nvim/extmark_defs.h"
#include "nvim/highlight_defs.h"
#include "nvim/map_defs.h"
+#include "nvim/tui/input_defs.h"
#include "nvim/ui_client.h"
#if defined(__NetBSD__)
@@ -34,6 +35,7 @@
// NOTE: Keys AND values must be allocated! khash.h does not make a copy.
//
MAP_DECLS(int, int)
+MAP_DECLS(int, cstr_t)
MAP_DECLS(cstr_t, ptr_t)
MAP_DECLS(cstr_t, int)
MAP_DECLS(ptr_t, ptr_t)
@@ -50,6 +52,8 @@ MAP_DECLS(int, String)
MAP_DECLS(ColorKey, ColorItem)
+MAP_DECLS(KittyKey, cstr_t)
+
#define MAP_INIT { { 0, 0, 0, 0, NULL, NULL, NULL } }
#define map_init(k, v, map) do { *(map) = (Map(k, v)) MAP_INIT; } while (false)
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index ff7a3da5c3..5a11ac686e 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -1266,7 +1266,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
continue;
}
- if (vim_regexec(regmatch, p, (colnr_T)0)) {
+ if (vim_regexec(regmatch, (char *)p, (colnr_T)0)) {
if (round == 1) {
count++;
} else {
@@ -1289,7 +1289,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
for (; mp; mp = mp->m_next) {
if (mp->m_mode & expand_mapmodes) {
p = translate_mapping(mp->m_keys, CPO_TO_CPO_FLAGS);
- if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) {
+ if (p != NULL && vim_regexec(regmatch, (char *)p, (colnr_T)0)) {
if (round == 1) {
count++;
} else {
@@ -2113,20 +2113,6 @@ void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_maparg(argvars, rettv, false);
}
-void init_default_mappings(void)
-{
- add_map("Y", "y$", MODE_NORMAL, false);
-
- // Use normal! <C-L> to prevent inserting raw <C-L> when using i_<C-O>
- // See https://github.com/neovim/neovim/issues/17473
- add_map("<C-L>", "<Cmd>nohlsearch<Bar>diffupdate<Bar>normal! <C-L><CR>",
- MODE_NORMAL, false);
- add_map("<C-U>", "<C-G>u<C-U>", MODE_INSERT, false);
- add_map("<C-W>", "<C-G>u<C-W>", MODE_INSERT, false);
- add_map("*", "y/\\\\V<C-R>\"<CR>", MODE_VISUAL, false);
- add_map("#", "y?\\\\V<C-R>\"<CR>", MODE_VISUAL, false);
-}
-
/// Add a mapping. Unlike @ref do_map this copies the string arguments, so
/// static or read-only strings can be used.
///
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index a0fed19a98..66855c66b5 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -13,7 +13,9 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/extmark.h"
@@ -24,6 +26,7 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
@@ -61,7 +64,8 @@ static xfmark_T namedfm[NGLOBALMARKS];
*/
int setmark(int c)
{
- return setmark_pos(c, &curwin->w_cursor, curbuf->b_fnum);
+ fmarkv_T view = mark_view_make(curwin->w_topline, curwin->w_cursor);
+ return setmark_pos(c, &curwin->w_cursor, curbuf->b_fnum, &view);
}
/// Free fmark_T item
@@ -90,9 +94,10 @@ void clear_fmark(fmark_T *fm)
* When "c" is upper case use file "fnum".
* Returns OK on success, FAIL if bad name given.
*/
-int setmark_pos(int c, pos_T *pos, int fnum)
+int setmark_pos(int c, pos_T *pos, int fnum, fmarkv_T *view_pt)
{
int i;
+ fmarkv_T view = view_pt != NULL ? *view_pt : (fmarkv_T)INIT_FMARKV;
// Check for a special key (may cause islower() to crash).
if (c < 0) {
@@ -117,7 +122,7 @@ int setmark_pos(int c, pos_T *pos, int fnum)
}
if (c == '"') {
- RESET_FMARK(&buf->b_last_cursor, *pos, buf->b_fnum);
+ RESET_FMARK(&buf->b_last_cursor, *pos, buf->b_fnum, view);
return OK;
}
@@ -147,7 +152,7 @@ int setmark_pos(int c, pos_T *pos, int fnum)
if (ASCII_ISLOWER(c)) {
i = c - 'a';
- RESET_FMARK(buf->b_namedm + i, *pos, fnum);
+ RESET_FMARK(buf->b_namedm + i, *pos, fnum, view);
return OK;
}
if (ASCII_ISUPPER(c) || ascii_isdigit(c)) {
@@ -156,7 +161,7 @@ int setmark_pos(int c, pos_T *pos, int fnum)
} else {
i = c - 'A';
}
- RESET_XFMARK(namedfm + i, *pos, fnum, NULL);
+ RESET_XFMARK(namedfm + i, *pos, fnum, view, NULL);
return OK;
}
return FAIL;
@@ -202,7 +207,8 @@ void setpcmark(void)
curwin->w_jumplistidx = curwin->w_jumplistlen;
fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
- SET_XFMARK(fm, curwin->w_pcmark, curbuf->b_fnum, NULL);
+ fmarkv_T view = mark_view_make(curwin->w_topline, curwin->w_pcmark);
+ SET_XFMARK(fm, curwin->w_pcmark, curbuf->b_fnum, view, NULL);
}
/*
@@ -221,245 +227,407 @@ void checkpcmark(void)
curwin->w_prev_pcmark.lnum = 0; // it has been checked
}
-/*
- * move "count" positions in the jump list (count may be negative)
- */
-pos_T *movemark(int count)
+/// Get mark in "count" position in the |jumplist| relative to the current index.
+///
+/// If the mark is in a different buffer, it will be skipped unless the buffer exists.
+///
+/// @note cleanup_jumplist() is run, which removes duplicate marks, and
+/// changes win->w_jumplistidx.
+/// @param[in] win window to get jumplist from.
+/// @param[in] count count to move may be negative.
+///
+/// @return mark, NULL if out of jumplist bounds.
+fmark_T *get_jumplist(win_T *win, int count)
{
- pos_T *pos;
- xfmark_T *jmp;
+ xfmark_T *jmp = NULL;
- cleanup_jumplist(curwin, true);
+ cleanup_jumplist(win, true);
- if (curwin->w_jumplistlen == 0) { // nothing to jump to
- return (pos_T *)NULL;
+ if (win->w_jumplistlen == 0) { // nothing to jump to
+ return NULL;
}
for (;;) {
- if (curwin->w_jumplistidx + count < 0
- || curwin->w_jumplistidx + count >= curwin->w_jumplistlen) {
- return (pos_T *)NULL;
+ if (win->w_jumplistidx + count < 0
+ || win->w_jumplistidx + count >= win->w_jumplistlen) {
+ return NULL;
}
- /*
- * if first CTRL-O or CTRL-I command after a jump, add cursor position
- * to list. Careful: If there are duplicates (CTRL-O immediately after
- * starting Vim on a file), another entry may have been removed.
- */
- if (curwin->w_jumplistidx == curwin->w_jumplistlen) {
+ // if first CTRL-O or CTRL-I command after a jump, add cursor position
+ // to list. Careful: If there are duplicates (CTRL-O immediately after
+ // starting Vim on a file), another entry may have been removed.
+ if (win->w_jumplistidx == win->w_jumplistlen) {
setpcmark();
- --curwin->w_jumplistidx; // skip the new entry
- if (curwin->w_jumplistidx + count < 0) {
- return (pos_T *)NULL;
+ win->w_jumplistidx--; // skip the new entry
+ if (win->w_jumplistidx + count < 0) {
+ return NULL;
}
}
- curwin->w_jumplistidx += count;
+ win->w_jumplistidx += count;
- jmp = curwin->w_jumplist + curwin->w_jumplistidx;
+ jmp = win->w_jumplist + win->w_jumplistidx;
if (jmp->fmark.fnum == 0) {
+ // Resolve the fnum (buff number) in the mark before returning it (shada)
fname2fnum(jmp);
}
if (jmp->fmark.fnum != curbuf->b_fnum) {
- // jump to other file
- if (buflist_findnr(jmp->fmark.fnum) == NULL) { // Skip this one ..
+ // Needs to switch buffer, if it can't find it skip the mark
+ if (buflist_findnr(jmp->fmark.fnum) == NULL) {
count += count < 0 ? -1 : 1;
continue;
}
- if (buflist_getfile(jmp->fmark.fnum, jmp->fmark.mark.lnum,
- 0, FALSE) == FAIL) {
- return (pos_T *)NULL;
- }
- // Set lnum again, autocommands my have changed it
- curwin->w_cursor = jmp->fmark.mark;
- pos = (pos_T *)-1;
- } else {
- pos = &(jmp->fmark.mark);
}
- return pos;
+ break;
}
+ return &jmp->fmark;
}
-/*
- * Move "count" positions in the changelist (count may be negative).
- */
-pos_T *movechangelist(int count)
+/// Get mark in "count" position in the |changelist| relative to the current index.
+///
+/// @note Changes the win->w_changelistidx.
+/// @param[in] win window to get jumplist from.
+/// @param[in] count count to move may be negative.
+///
+/// @return mark, NULL if out of bounds.
+fmark_T *get_changelist(buf_T *buf, win_T *win, int count)
{
int n;
+ fmark_T *fm;
- if (curbuf->b_changelistlen == 0) { // nothing to jump to
- return (pos_T *)NULL;
+ if (buf->b_changelistlen == 0) { // nothing to jump to
+ return NULL;
}
- n = curwin->w_changelistidx;
+ n = win->w_changelistidx;
if (n + count < 0) {
if (n == 0) {
- return (pos_T *)NULL;
+ return NULL;
}
n = 0;
- } else if (n + count >= curbuf->b_changelistlen) {
- if (n == curbuf->b_changelistlen - 1) {
- return (pos_T *)NULL;
+ } else if (n + count >= buf->b_changelistlen) {
+ if (n == buf->b_changelistlen - 1) {
+ return NULL;
}
- n = curbuf->b_changelistlen - 1;
+ n = buf->b_changelistlen - 1;
} else {
n += count;
}
- curwin->w_changelistidx = n;
- return &(curbuf->b_changelist[n].mark);
+ win->w_changelistidx = n;
+ fm = &(buf->b_changelist[n]);
+ // Changelist marks are always buffer local, Shada does not set it when loading
+ fm->fnum = curbuf->handle;
+ return &(buf->b_changelist[n]);
}
-/*
- * Find mark "c" in buffer pointed to by "buf".
- * If "changefile" is TRUE it's allowed to edit another file for '0, 'A, etc.
- * If "fnum" is not NULL store the fnum there for '0, 'A etc., don't edit
- * another file.
- * Returns:
- * - pointer to pos_T if found. lnum is 0 when mark not set, -1 when mark is
- * in another file which can't be gotten. (caller needs to check lnum!)
- * - NULL if there is no mark called 'c'.
- * - -1 if mark is in other file and jumped there (only if changefile is TRUE)
- */
-pos_T *getmark_buf(buf_T *buf, int c, bool changefile)
+/// Get a named mark.
+///
+/// All types of marks, even those that are not technically a mark will be returned as such. Use
+/// mark_move_to() to move to the mark.
+/// @note Some of the pointers are statically allocated, if in doubt make a copy. For more
+/// information read mark_get_local().
+/// @param buf Buffer to get the mark from.
+/// @param win Window to get or calculate the mark from (motion type marks, context mark).
+/// @param fmp[out] Optional pointer to store the result in, as a workaround for the note above.
+/// @param flag MarkGet value
+/// @param name Name of the mark.
+///
+/// @return Mark if found, otherwise NULL. For @c kMarkBufLocal, NULL is returned
+/// when no mark is found in @a buf.
+fmark_T *mark_get(buf_T *buf, win_T *win, fmark_T *fmp, MarkGet flag, int name)
{
- return getmark_buf_fnum(buf, c, changefile, NULL);
+ fmark_T *fm = NULL;
+ if (ASCII_ISUPPER(name) || ascii_isdigit(name)) {
+ // Global marks
+ xfmark_T *xfm = mark_get_global(!(flag & kMarkAllNoResolve), name);
+ fm = &xfm->fmark;
+ // Only wanted marks belonging to the buffer
+ if ((flag & kMarkBufLocal) && xfm->fmark.fnum != buf->handle) {
+ return NULL;
+ }
+ } else if (name > 0 && name < NMARK_LOCAL_MAX) {
+ // Local Marks
+ fm = mark_get_local(buf, win, name);
+ }
+ if (fmp != NULL && fm != NULL) {
+ *fmp = *fm;
+ return fmp;
+ }
+ return fm;
}
-pos_T *getmark(int c, bool changefile)
+/// Get a global mark {A-Z0-9}.
+///
+/// @param name the name of the mark.
+/// @param resolve Whether to try resolving the mark fnum (i.e., load the buffer stored in
+/// the mark fname and update the xfmark_T (expensive)).
+///
+/// @return Mark
+xfmark_T *mark_get_global(bool resolve, int name)
{
- return getmark_buf_fnum(curbuf, c, changefile, NULL);
+ xfmark_T *mark;
+
+ if (ascii_isdigit(name)) {
+ name = name - '0' + NMARKS;
+ } else if (ASCII_ISUPPER(name)) {
+ name -= 'A';
+ } else {
+ // Not a valid mark name
+ assert(false);
+ }
+ mark = &namedfm[name];
+
+ if (resolve && mark->fmark.fnum == 0) {
+ // Resolve filename to fnum (SHADA marks)
+ fname2fnum(mark);
+ }
+ return mark;
}
-pos_T *getmark_buf_fnum(buf_T *buf, int c, bool changefile, int *fnum)
+/// Get a local mark (lowercase and symbols).
+///
+/// Some marks are not actually marks, but positions that are never adjusted or motions presented as
+/// marks. Search first for marks and fallback to finding motion type marks. If it's known
+/// ahead of time that the mark is actually a motion use the mark_get_motion() directly.
+///
+/// @note Lowercase, last_cursor '"', last insert '^', last change '.' are not statically
+/// allocated, everything else is.
+/// @param name the name of the mark.
+/// @param win window to retrieve marks that belong to it (motions and context mark).
+/// @param buf buf to retrieve marks that belong to it.
+///
+/// @return Mark, NULL if not found.
+fmark_T *mark_get_local(buf_T *buf, win_T *win, int name)
{
- pos_T *posp;
- pos_T *startp, *endp;
- static pos_T pos_copy;
+ fmark_T *mark = NULL;
+ if (ASCII_ISLOWER(name)) {
+ // normal named mark
+ mark = &buf->b_namedm[name - 'a'];
+ // to start of previous operator
+ } else if (name == '[') {
+ mark = pos_to_mark(buf, NULL, buf->b_op_start);
+ // to end of previous operator
+ } else if (name == ']') {
+ mark = pos_to_mark(buf, NULL, buf->b_op_end);
+ // visual marks
+ } else if (name == '<' || name == '>') {
+ mark = mark_get_visual(buf, name);
+ // previous context mark
+ } else if (name == '\'' || name == '`') {
+ // TODO(muniter): w_pcmark should be stored as a mark, but causes a nasty bug.
+ mark = pos_to_mark(curbuf, NULL, win->w_pcmark);
+ // to position when leaving buffer
+ } else if (name == '"') {
+ mark = &(buf->b_last_cursor);
+ // to where last Insert mode stopped
+ } else if (name == '^') {
+ mark = &(buf->b_last_insert);
+ // to where last change was made
+ } else if (name == '.') {
+ mark = &buf->b_last_change;
+ // Mark that are actually not marks but motions, e.g {, }, (, ), ...
+ } else {
+ mark = mark_get_motion(buf, win, name);
+ }
- posp = NULL;
+ if (mark) {
+ mark->fnum = buf->b_fnum;
+ }
- // Check for special key, can't be a mark name and might cause islower()
- // to crash.
- if (c < 0) {
- return posp;
- }
- if (c > '~') { // check for islower()/isupper()
- } else if (c == '\'' || c == '`') { // previous context mark
- pos_copy = curwin->w_pcmark; // need to make a copy because
- posp = &pos_copy; // w_pcmark may be changed soon
- } else if (c == '"') { // to pos when leaving buffer
- posp = &(buf->b_last_cursor.mark);
- } else if (c == '^') { // to where Insert mode stopped
- posp = &(buf->b_last_insert.mark);
- } else if (c == '.') { // to where last change was made
- posp = &(buf->b_last_change.mark);
- } else if (c == '[') { // to start of previous operator
- posp = &(buf->b_op_start);
- } else if (c == ']') { // to end of previous operator
- posp = &(buf->b_op_end);
- } else if (c == '{' || c == '}') { // to previous/next paragraph
- pos_T pos;
+ return mark;
+}
+
+/// Get marks that are actually motions but return them as marks
+///
+/// Gets the following motions as marks: '{', '}', '(', ')'
+/// @param name name of the mark
+/// @param win window to retrieve the cursor to calculate the mark.
+/// @param buf buf to wrap motion marks with it's buffer number (fm->fnum).
+///
+/// @return[static] Mark.
+fmark_T *mark_get_motion(buf_T *buf, win_T *win, int name)
+{
+ fmark_T *mark = NULL;
+ const pos_T pos = curwin->w_cursor;
+ const bool slcb = listcmd_busy;
+ listcmd_busy = true; // avoid that '' is changed
+ if (name == '{' || name == '}') { // to previous/next paragraph
oparg_T oa;
- bool slcb = listcmd_busy;
-
- pos = curwin->w_cursor;
- listcmd_busy = true; // avoid that '' is changed
- if (findpar(&oa.inclusive,
- c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE)) {
- pos_copy = curwin->w_cursor;
- posp = &pos_copy;
+ if (findpar(&oa.inclusive, name == '}' ? FORWARD : BACKWARD, 1L, NUL, false)) {
+ mark = pos_to_mark(buf, NULL, win->w_cursor);
}
- curwin->w_cursor = pos;
- listcmd_busy = slcb;
- } else if (c == '(' || c == ')') { // to previous/next sentence
- pos_T pos;
- bool slcb = listcmd_busy;
-
- pos = curwin->w_cursor;
- listcmd_busy = true; // avoid that '' is changed
- if (findsent(c == ')' ? FORWARD : BACKWARD, 1L)) {
- pos_copy = curwin->w_cursor;
- posp = &pos_copy;
+ } else if (name == '(' || name == ')') { // to previous/next sentence
+ if (findsent(name == ')' ? FORWARD : BACKWARD, 1L)) {
+ mark = pos_to_mark(buf, NULL, win->w_cursor);
}
- curwin->w_cursor = pos;
- listcmd_busy = slcb;
- } else if (c == '<' || c == '>') { // start/end of visual area
- startp = &buf->b_visual.vi_start;
- endp = &buf->b_visual.vi_end;
- if (((c == '<') == lt(*startp, *endp) || endp->lnum == 0)
- && startp->lnum != 0) {
- posp = startp;
+ }
+ curwin->w_cursor = pos;
+ listcmd_busy = slcb;
+ return mark;
+}
+
+/// Get visual marks '<', '>'
+///
+/// This marks are different to normal marks:
+/// 1. Never adjusted.
+/// 2. Different behavior depending on editor state (visual mode).
+/// 3. Not saved in shada.
+/// 4. Re-ordered when defined in reverse.
+/// @param buf Buffer to get the mark from.
+/// @param name Mark name '<' or '>'.
+///
+/// @return[static] Mark
+fmark_T *mark_get_visual(buf_T *buf, int name)
+{
+ fmark_T *mark = NULL;
+ if (name == '<' || name == '>') {
+ // start/end of visual area
+ pos_T startp = buf->b_visual.vi_start;
+ pos_T endp = buf->b_visual.vi_end;
+ if (((name == '<') == lt(startp, endp) || endp.lnum == 0)
+ && startp.lnum != 0) {
+ mark = pos_to_mark(buf, NULL, startp);
} else {
- posp = endp;
+ mark = pos_to_mark(buf, NULL, endp);
}
- // For Visual line mode, set mark at begin or end of line
- if (buf->b_visual.vi_mode == 'V') {
- pos_copy = *posp;
- posp = &pos_copy;
- if (c == '<') {
- pos_copy.col = 0;
+ if (mark != NULL && buf->b_visual.vi_mode == 'V') {
+ if (name == '<') {
+ mark->mark.col = 0;
} else {
- pos_copy.col = MAXCOL;
+ mark->mark.col = MAXCOL;
}
- pos_copy.coladd = 0;
- }
- } else if (ASCII_ISLOWER(c)) { // normal named mark
- posp = &(buf->b_namedm[c - 'a'].mark);
- } else if (ASCII_ISUPPER(c) || ascii_isdigit(c)) { // named file mark
- if (ascii_isdigit(c)) {
- c = c - '0' + NMARKS;
- } else {
- c -= 'A';
+ mark->mark.coladd = 0;
}
- posp = &(namedfm[c].fmark.mark);
+ }
+ return mark;
+}
+
+/// Wrap a pos_T into an fmark_T, used to abstract marks handling.
+///
+/// Pass an fmp if multiple c
+/// @note view fields are set to 0.
+/// @param buf for fmark->fnum.
+/// @param pos for fmrak->mark.
+/// @param fmp pointer to save the mark.
+///
+/// @return[static] Mark with the given information.
+fmark_T *pos_to_mark(buf_T *buf, fmark_T *fmp, pos_T pos)
+{
+ static fmark_T fms = INIT_FMARK;
+ fmark_T *fm = fmp == NULL ? &fms : fmp;
+ fm->fnum = buf->handle;
+ fm->mark = pos;
+ return fm;
+}
+
+/// Attempt to switch to the buffer of the given global mark
+///
+/// @param fm
+/// @param pcmark_on_switch leave a context mark when switching buffer.
+/// @return whether the buffer was switched or not.
+static MarkMoveRes switch_to_mark_buf(fmark_T *fm, bool pcmark_on_switch)
+{
+ bool res;
+ if (fm->fnum != curbuf->b_fnum) {
+ // Switch to another file.
+ int getfile_flag = pcmark_on_switch ? GETF_SETMARK : 0;
+ res = buflist_getfile(fm->fnum, (linenr_T)1, getfile_flag, false) == OK;
+ return res == true ? kMarkSwitchedBuf : kMarkMoveFailed;
+ }
+ return 0;
+}
- if (namedfm[c].fmark.fnum == 0) {
- fname2fnum(&namedfm[c]);
+/// Move to the given file mark, changing the buffer and cursor position.
+///
+/// Validate the mark, switch to the buffer, and move the cursor.
+/// @param fm Mark, can be NULL will raise E78: Unknown mark
+/// @param flags MarkMove flags to configure the movement to the mark.
+///
+/// @return MarkMovekRes flags representing the outcome
+MarkMoveRes mark_move_to(fmark_T *fm, MarkMove flags)
+{
+ static fmark_T fm_copy = INIT_FMARK;
+ MarkMoveRes res = kMarkMoveSuccess;
+ if (!mark_check(fm)) {
+ res = kMarkMoveFailed;
+ goto end;
+ }
+
+ if (fm->fnum != curbuf->handle) {
+ // Need to change buffer
+ fm_copy = *fm; // Copy, autocommand may change it
+ fm = &fm_copy;
+ res |= switch_to_mark_buf(fm, !(flags & kMarkJumpList));
+ // Failed switching buffer
+ if (res & kMarkMoveFailed) {
+ goto end;
+ }
+ // Check line count now that the **destination buffer is loaded**.
+ if (!mark_check_line_bounds(curbuf, fm)) {
+ res |= kMarkMoveFailed;
+ goto end;
}
+ } else if (flags & kMarkContext) {
+ // Doing it in this condition avoids double context mark when switching buffer.
+ setpcmark();
+ }
+ // Move the cursor while keeping track of what changed for the caller
+ pos_T prev_pos = curwin->w_cursor;
+ pos_T pos = fm->mark;
+ curwin->w_cursor = fm->mark;
+ if (flags & kMarkBeginLine) {
+ beginline(BL_WHITE | BL_FIX);
+ }
+ res = prev_pos.lnum != pos.lnum ? res | kMarkChangedLine | kMarkChangedCursor : res;
+ res = prev_pos.col != pos.col ? res | kMarkChangedCol | kMarkChangedCursor : res;
+ if (flags & kMarkSetView) {
+ mark_view_restore(fm);
+ }
- if (fnum != NULL) {
- *fnum = namedfm[c].fmark.fnum;
- } else if (namedfm[c].fmark.fnum != buf->b_fnum) {
- // mark is in another file
- posp = &pos_copy;
-
- if (namedfm[c].fmark.mark.lnum != 0
- && changefile && namedfm[c].fmark.fnum) {
- if (buflist_getfile(namedfm[c].fmark.fnum,
- (linenr_T)1, GETF_SETMARK, FALSE) == OK) {
- // Set the lnum now, autocommands could have changed it
- curwin->w_cursor = namedfm[c].fmark.mark;
- return (pos_T *)-1;
- }
- pos_copy.lnum = -1; // can't get file
- } else {
- pos_copy.lnum = 0; // mark exists, but is not valid in current buffer
- }
+ if (res & kMarkSwitchedBuf || res & kMarkChangedCursor) {
+ check_cursor();
+ }
+end:
+ return res;
+}
+
+/// Restore the mark view.
+/// By remembering the offset between topline and mark lnum at the time of
+/// definition, this function restores the "view".
+/// @note Assumes the mark has been checked, is valid.
+/// @param fm the named mark.
+void mark_view_restore(fmark_T *fm)
+{
+ if (fm != NULL && fm->view.topline_offset >= 0) {
+ linenr_T topline = fm->mark.lnum - fm->view.topline_offset;
+ // If the mark does not have a view, topline_offset is MAXLNUM,
+ // and this check can prevent restoring mark view in that case.
+ if (topline >= 1) {
+ set_topline(curwin, topline);
}
}
+}
- return posp;
+fmarkv_T mark_view_make(linenr_T topline, pos_T pos)
+{
+ return (fmarkv_T){ pos.lnum - topline };
}
-/// Search for the next named mark in the current file.
+/// Search for the next named mark in the current file from a start position.
///
-/// @param startpos where to start
-/// @param dir direction for search
+/// @param startpos where to start.
+/// @param dir direction for search.
///
-/// @return pointer to pos_T of the next mark or NULL if no mark is found.
-pos_T *getnextmark(pos_T *startpos, int dir, int begin_line)
+/// @return next mark or NULL if no mark is found.
+fmark_T *getnextmark(pos_T *startpos, int dir, int begin_line)
{
int i;
- pos_T *result = NULL;
+ fmark_T *result = NULL;
pos_T pos;
pos = *startpos;
- // When searching backward and leaving the cursor on the first non-blank,
- // position must be in a previous line.
- // When searching forward and leaving the cursor on the first non-blank,
- // position must be in a next line.
if (dir == BACKWARD && begin_line) {
pos.col = 0;
} else if (dir == FORWARD && begin_line) {
@@ -469,14 +637,14 @@ pos_T *getnextmark(pos_T *startpos, int dir, int begin_line)
for (i = 0; i < NMARKS; i++) {
if (curbuf->b_namedm[i].mark.lnum > 0) {
if (dir == FORWARD) {
- if ((result == NULL || lt(curbuf->b_namedm[i].mark, *result))
+ if ((result == NULL || lt(curbuf->b_namedm[i].mark, result->mark))
&& lt(pos, curbuf->b_namedm[i].mark)) {
- result = &curbuf->b_namedm[i].mark;
+ result = &curbuf->b_namedm[i];
}
} else {
- if ((result == NULL || lt(*result, curbuf->b_namedm[i].mark))
+ if ((result == NULL || lt(result->mark, curbuf->b_namedm[i].mark))
&& lt(curbuf->b_namedm[i].mark, pos)) {
- result = &curbuf->b_namedm[i].mark;
+ result = &curbuf->b_namedm[i];
}
}
}
@@ -518,7 +686,7 @@ static void fname2fnum(xfmark_T *fm)
p = path_shorten_fname(NameBuff, IObuff);
// buflist_new() will call fmarks_check_names()
- (void)buflist_new(NameBuff, p, (linenr_T)1, 0);
+ (void)buflist_new((char *)NameBuff, (char *)p, (linenr_T)1, 0);
}
}
@@ -529,7 +697,7 @@ static void fname2fnum(xfmark_T *fm)
*/
void fmarks_check_names(buf_T *buf)
{
- char_u *name = buf->b_ffname;
+ char_u *name = (char_u *)buf->b_ffname;
int i;
if (buf->b_ffname == NULL) {
@@ -557,29 +725,48 @@ static void fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf)
}
}
-/*
- * Check a if a position from a mark is valid.
- * Give and error message and return FAIL if not.
- */
-int check_mark(pos_T *pos)
+/// Check the position in @a fm is valid.
+///
+/// Emit error message and return accordingly.
+///
+/// Checks for:
+/// - NULL raising unknown mark error.
+/// - Line number <= 0 raising mark not set.
+/// - Line number > buffer line count, raising invalid mark.
+/// @param fm[in] File mark to check.
+///
+/// @return true if the mark passes all the above checks, else false.
+bool mark_check(fmark_T *fm)
{
- if (pos == NULL) {
+ if (fm == NULL) {
emsg(_(e_umark));
- return FAIL;
- }
- if (pos->lnum <= 0) {
- // lnum is negative if mark is in another file can can't get that
- // file, error message already give then.
- if (pos->lnum == 0) {
+ return false;
+ } else if (fm->mark.lnum <= 0) {
+ // In both cases it's an error but only raise when equals to 0
+ if (fm->mark.lnum == 0) {
emsg(_(e_marknotset));
}
- return FAIL;
+ return false;
}
- if (pos->lnum > curbuf->b_ml.ml_line_count) {
+ // Only check for valid line number if the buffer is loaded.
+ if (fm->fnum == curbuf->handle && !mark_check_line_bounds(curbuf, fm)) {
+ return false;
+ }
+ return true;
+}
+
+/// Check if a mark line number is greater than the buffer line count, and set e_markinval.
+/// @note Should be done after the buffer is loaded into memory.
+/// @param buf Buffer where the mark is set.
+/// @param fm Mark to check.
+/// @return true if below line count else false.
+bool mark_check_line_bounds(buf_T *buf, fmark_T *fm)
+{
+ if (buf != NULL && fm->mark.lnum > buf->b_ml.ml_line_count) {
emsg(_(e_markinval));
- return FAIL;
+ return false;
}
- return OK;
+ return true;
}
/// Clear all marks and change list in the given buffer
@@ -615,7 +802,7 @@ char_u *fm_getname(fmark_T *fmark, int lead_len)
if (fmark->fnum == curbuf->b_fnum) { // current buffer
return mark_line(&(fmark->mark), lead_len);
}
- return buflist_nr2name(fmark->fnum, FALSE, TRUE);
+ return (char_u *)buflist_nr2name(fmark->fnum, false, true);
}
/*
@@ -1318,7 +1505,7 @@ void copy_jumplist(win_T *from, win_T *to)
/// Iterate over jumplist items
///
-/// @warning No jumplist-editing functions must be run while iteration is in
+/// @warning No jumplist-editing functions must be called while iteration is in
/// progress.
///
/// @param[in] iter Iterator. Pass NULL to start iteration.
@@ -1331,7 +1518,7 @@ const void *mark_jumplist_iter(const void *const iter, const win_T *const win, x
FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
if (iter == NULL && win->w_jumplistlen == 0) {
- *fm = (xfmark_T) { { { 0, 0, 0 }, 0, 0, NULL }, NULL };
+ *fm = (xfmark_T)INIT_XFMARK;
return NULL;
}
const xfmark_T *const iter_mark =
@@ -1348,7 +1535,7 @@ const void *mark_jumplist_iter(const void *const iter, const win_T *const win, x
/// Iterate over global marks
///
-/// @warning No mark-editing functions must be run while iteration is in
+/// @warning No mark-editing functions must be called while iteration is in
/// progress.
///
/// @param[in] iter Iterator. Pass NULL to start iteration.
@@ -1422,7 +1609,7 @@ static inline const fmark_T *next_buffer_mark(const buf_T *const buf, char *cons
/// Iterate over buffer marks
///
-/// @warning No mark-editing functions must be run while iteration is in
+/// @warning No mark-editing functions must be called while iteration is in
/// progress.
///
/// @param[in] iter Iterator. Pass NULL to start iteration.
@@ -1539,7 +1726,7 @@ void free_jumplist(win_T *wp)
void set_last_cursor(win_T *win)
{
if (win->w_buffer != NULL) {
- RESET_FMARK(&win->w_buffer->b_last_cursor, win->w_cursor, 0);
+ RESET_FMARK(&win->w_buffer->b_last_cursor, win->w_cursor, 0, ((fmarkv_T)INIT_FMARKV));
}
}
@@ -1640,9 +1827,10 @@ void get_buf_local_marks(const buf_T *buf, list_T *l)
/// Get a global mark
///
+/// @note Mark might not have it's fnum resolved.
/// @param[in] Name of named mark
/// @param[out] Global/file mark
-xfmark_T get_global_mark(char name)
+xfmark_T get_raw_global_mark(char name)
{
return namedfm[mark_global_index(name)];
}
@@ -1659,7 +1847,7 @@ void get_global_marks(list_T *l)
// Marks 'A' to 'Z' and '0' to '9'
for (int i = 0; i < NMARKS + EXTRA_MARKS; i++) {
if (namedfm[i].fmark.fnum != 0) {
- name = (char *)buflist_nr2name(namedfm[i].fmark.fnum, true, true);
+ name = buflist_nr2name(namedfm[i].fmark.fnum, true, true);
} else {
name = namedfm[i].fname;
}
diff --git a/src/nvim/mark.h b/src/nvim/mark.h
index a55f733d9a..6da976e8d3 100644
--- a/src/nvim/mark.h
+++ b/src/nvim/mark.h
@@ -13,42 +13,43 @@
#include "nvim/pos.h"
/// Set fmark using given value
-#define SET_FMARK(fmarkp_, mark_, fnum_) \
+#define SET_FMARK(fmarkp_, mark_, fnum_, view_) \
do { \
fmark_T *const fmarkp__ = fmarkp_; \
fmarkp__->mark = mark_; \
fmarkp__->fnum = fnum_; \
fmarkp__->timestamp = os_time(); \
+ fmarkp__->view = view_; \
fmarkp__->additional_data = NULL; \
} while (0)
/// Free and set fmark using given value
-#define RESET_FMARK(fmarkp_, mark_, fnum_) \
+#define RESET_FMARK(fmarkp_, mark_, fnum_, view_) \
do { \
fmark_T *const fmarkp___ = fmarkp_; \
free_fmark(*fmarkp___); \
- SET_FMARK(fmarkp___, mark_, fnum_); \
+ SET_FMARK(fmarkp___, mark_, fnum_, view_); \
} while (0)
/// Clear given fmark
#define CLEAR_FMARK(fmarkp_) \
- RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0)
+ RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0, ((fmarkv_T) { 0 }))
/// Set given extended mark (regular mark + file name)
-#define SET_XFMARK(xfmarkp_, mark_, fnum_, fname_) \
+#define SET_XFMARK(xfmarkp_, mark_, fnum_, view_, fname_) \
do { \
xfmark_T *const xfmarkp__ = xfmarkp_; \
xfmarkp__->fname = fname_; \
- SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_); \
+ SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_, view_); \
} while (0)
/// Free and set given extended mark (regular mark + file name)
-#define RESET_XFMARK(xfmarkp_, mark_, fnum_, fname_) \
+#define RESET_XFMARK(xfmarkp_, mark_, fnum_, view_, fname_) \
do { \
xfmark_T *const xfmarkp__ = xfmarkp_; \
free_xfmark(*xfmarkp__); \
xfmarkp__->fname = fname_; \
- SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_); \
+ SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_, view_); \
} while (0)
/// Convert mark name to the offset
diff --git a/src/nvim/mark_defs.h b/src/nvim/mark_defs.h
index 994ad30633..a78056c5f9 100644
--- a/src/nvim/mark_defs.h
+++ b/src/nvim/mark_defs.h
@@ -10,6 +10,33 @@
* (a normal mark is a lnum/col pair, the same as a file position)
*/
+/// Flags for outcomes when moving to a mark.
+typedef enum {
+ kMarkMoveSuccess = 1, ///< Successful move.
+ kMarkMoveFailed = 2, ///< Failed to move.
+ kMarkSwitchedBuf = 4, ///< Switched curbuf.
+ kMarkChangedCol = 8, ///< Changed the cursor col.
+ kMarkChangedLine = 16, ///< Changed the cursor line.
+ kMarkChangedCursor = 32, ///< Changed the cursor.
+ kMarkChangedView = 64, ///< Changed the view.
+} MarkMoveRes;
+
+/// Flags to configure the movement to a mark.
+typedef enum {
+ kMarkBeginLine = 1, ///< Move cursor to the beginning of the line.
+ kMarkContext = 2, ///< Leave context mark when moving the cursor.
+ KMarkNoContext = 4, ///< Don't leave a context mark.
+ kMarkSetView = 8, ///< Set the mark view after moving
+ kMarkJumpList = 16, ///< Special case, don't leave context mark when switching buffer
+} MarkMove;
+
+/// Options when getting a mark
+typedef enum {
+ kMarkBufLocal, ///< Only return marks that belong to the buffer.
+ kMarkAll, ///< Return all types of marks.
+ kMarkAllNoResolve, ///< Return all types of marks but don't resolve fnum (global marks).
+} MarkGet;
+
/// Number of possible numbered global marks
#define EXTRA_MARKS ('9' - '0' + 1)
@@ -25,24 +52,40 @@
/// but they are not saved in ShaDa files.
#define NLOCALMARKS (NMARKS + 3)
+/// Max value of local mark
+#define NMARK_LOCAL_MAX 126 // Index of '~'
+
/// Maximum number of marks in jump list
#define JUMPLISTSIZE 100
/// Maximum number of tags in tag stack
#define TAGSTACKSIZE 20
+/// Represents view in which the mark was created
+typedef struct fmarkv {
+ linenr_T topline_offset; ///< Amount of lines from the mark lnum to the top of the window.
+ ///< Use MAXLNUM to indicate that the mark does not have a view.
+} fmarkv_T;
+
+#define INIT_FMARKV { MAXLNUM }
+
/// Structure defining single local mark
typedef struct filemark {
pos_T mark; ///< Cursor position.
int fnum; ///< File number.
Timestamp timestamp; ///< Time when this mark was last set.
+ fmarkv_T view; ///< View the mark was created on
dict_T *additional_data; ///< Additional data from ShaDa file.
} fmark_T;
+#define INIT_FMARK { { 0, 0, 0 }, 0, 0, INIT_FMARKV, NULL }
+
/// Structure defining extended mark (mark with file name attached)
typedef struct xfilemark {
fmark_T fmark; ///< Actual mark.
char *fname; ///< File name, used when fnum == 0.
} xfmark_T;
+#define INIT_XFMARK { INIT_FMARK, NULL }
+
#endif // NVIM_MARK_DEFS_H
diff --git a/src/nvim/match.c b/src/nvim/match.c
index 3aa82527aa..e17a95569c 100644
--- a/src/nvim/match.c
+++ b/src/nvim/match.c
@@ -86,7 +86,7 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in
m = xcalloc(1, sizeof(matchitem_T));
m->id = id;
m->priority = prio;
- m->pattern = pat == NULL ? NULL: (char_u *)xstrdup(pat);
+ m->pattern = pat == NULL ? NULL: xstrdup(pat);
m->hlg_id = hlg_id;
m->match.regprog = regprog;
m->match.rmm_ic = false;
@@ -398,7 +398,7 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_
linenr_T l;
colnr_T matchcol;
long nmatched = 0;
- int save_called_emsg = called_emsg;
+ const int called_emsg_before = called_emsg;
// for :{range}s/pat only highlight inside the range
if (lnum < search_first_line || lnum > search_last_line) {
@@ -421,7 +421,6 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_
// Repeat searching for a match until one is found that includes "mincol"
// or none is found in this line.
- called_emsg = false;
for (;;) {
// Stop searching after passing the time limit.
if (profile_passed_limit(shl->tm)) {
@@ -468,7 +467,7 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_
if (regprog_is_copy) {
cur->match.regprog = cur->hl.rm.regprog;
}
- if (called_emsg || got_int || timed_out) {
+ if (called_emsg > called_emsg_before || got_int || timed_out) {
// Error while handling regexp: stop using this regexp.
if (shl == search_hl) {
// don't free regprog in the match list, it's a copy
@@ -494,9 +493,6 @@ static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_
shl->lnum += shl->rm.startpos[0].lnum;
break; // useful match found
}
-
- // Restore called_emsg for assert_fails().
- called_emsg = save_called_emsg;
}
}
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 1032986a02..a9792cf1b9 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -1840,8 +1840,9 @@ int mb_off_next(const char_u *base, const char_u *p)
/// Return the offset from "p" to the last byte of the character it points
/// into. Can start anywhere in a stream of bytes.
/// Composing characters are not included.
-int mb_tail_off(const char_u *base, const char_u *p)
+int mb_tail_off(const char *base, const char *p_in)
{
+ const uint8_t *p = (uint8_t *)p_in;
int i;
int j;
@@ -1853,7 +1854,7 @@ int mb_tail_off(const char_u *base, const char_u *p)
for (i = 0; (p[i + 1] & 0xc0) == 0x80; i++) {}
// Check for illegal sequence.
- for (j = 0; p - j > base; j++) {
+ for (j = 0; p_in - j > base; j++) {
if ((p[-j] & 0xc0) != 0x80) {
break;
}
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index eb6389a760..1ea5e2ccdc 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -665,7 +665,7 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf)
* First replace home dir path with "~/" with home_replace().
* Then insert the user name to get "~user/".
*/
- home_replace(NULL, (char *)buf->b_ffname, (char *)b0p->b0_fname,
+ home_replace(NULL, buf->b_ffname, (char *)b0p->b0_fname,
B0_FNAME_SIZE_CRYPT, true);
if (b0p->b0_fname[0] == '~') {
// If there is no user name or it is too long, don't use "~/"
@@ -680,7 +680,7 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf)
}
}
FileInfo file_info;
- if (os_fileinfo((char *)buf->b_ffname, &file_info)) {
+ if (os_fileinfo(buf->b_ffname, &file_info)) {
long_to_char(file_info.stat.st_mtim.tv_sec, b0p->b0_mtime);
long_to_char((long)os_fileinfo_inode(&file_info), b0p->b0_ino);
buf_store_file_info(buf, &file_info);
@@ -708,7 +708,7 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf)
/// not set.
static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf)
{
- if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname)) {
+ if (same_directory(buf->b_ml.ml_mfp->mf_fname, (char_u *)buf->b_ffname)) {
b0p->b0_flags |= B0_SAME_DIR;
} else {
b0p->b0_flags &= (uint8_t) ~B0_SAME_DIR;
@@ -943,7 +943,7 @@ void ml_recover(bool checkext)
if (buf_spname(curbuf) != NULL) {
STRLCPY(NameBuff, buf_spname(curbuf), MAXPATHL);
} else {
- home_replace(NULL, (char *)curbuf->b_ffname, (char *)NameBuff, MAXPATHL, true);
+ home_replace(NULL, curbuf->b_ffname, (char *)NameBuff, MAXPATHL, true);
}
smsg(_("Original file \"%s\""), NameBuff);
msg_putchar('\n');
@@ -955,7 +955,7 @@ void ml_recover(bool checkext)
FileInfo swp_file_info;
mtime = char_to_long(b0p->b0_mtime);
if (curbuf->b_ffname != NULL
- && os_fileinfo((char *)curbuf->b_ffname, &org_file_info)
+ && os_fileinfo(curbuf->b_ffname, &org_file_info)
&& ((os_fileinfo((char *)mfp->mf_fname, &swp_file_info)
&& org_file_info.stat.st_mtim.tv_sec
> swp_file_info.stat.st_mtim.tv_sec)
@@ -989,7 +989,7 @@ void ml_recover(bool checkext)
* 'fileencoding', etc. Ignore errors. The text itself is not used.
*/
if (curbuf->b_ffname != NULL) {
- orig_file_status = readfile((char *)curbuf->b_ffname, NULL, (linenr_T)0,
+ orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0,
(linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW, false);
}
@@ -1063,7 +1063,7 @@ void ml_recover(bool checkext)
*/
if (!cannot_open) {
line_count = pp->pb_pointer[idx].pe_line_count;
- if (readfile((char *)curbuf->b_ffname, NULL, lnum,
+ if (readfile(curbuf->b_ffname, NULL, lnum,
pp->pb_pointer[idx].pe_old_lnum - 1, line_count,
NULL, 0, false) != OK) {
cannot_open = true;
@@ -1304,7 +1304,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
// Isolate a directory name from *dirp and put it in dir_name (we know
// it is large enough, so use 31000 for length).
// Advance dirp to next directory name.
- (void)copy_option_part(&dirp, dir_name, 31000, ",");
+ (void)copy_option_part((char **)&dirp, (char *)dir_name, 31000, ",");
if (dir_name[0] == '.' && dir_name[1] == NUL) { // check current dir
if (fname == NULL) {
@@ -1693,7 +1693,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
* call ml_preserve() to get rid of all negative numbered blocks.
*/
FileInfo file_info;
- if (!os_fileinfo((char *)buf->b_ffname, &file_info)
+ if (!os_fileinfo(buf->b_ffname, &file_info)
|| file_info.stat.st_mtim.tv_sec != buf->b_mtime_read
|| file_info.stat.st_mtim.tv_nsec != buf->b_mtime_read_ns
|| os_fileinfo_size(&file_info) != buf->b_orig_size) {
@@ -1833,6 +1833,7 @@ char_u *ml_get_buf(buf_T *buf, linenr_T lnum, bool will_change)
DATA_BL *dp;
char_u *ptr;
static int recursive = 0;
+ static char_u questions[4];
if (lnum > buf->b_ml.ml_line_count) { // invalid line number
if (recursive == 0) {
@@ -1842,9 +1843,12 @@ char_u *ml_get_buf(buf_T *buf, linenr_T lnum, bool will_change)
siemsg(_("E315: ml_get: invalid lnum: %" PRId64), (int64_t)lnum);
recursive--;
}
+ ml_flush_line(buf);
+ buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
errorret:
- STRCPY(IObuff, "???");
- return IObuff;
+ STRCPY(questions, "???");
+ buf->b_ml.ml_line_lnum = lnum;
+ return questions;
}
if (lnum <= 0) { // pretend line 0 is line 1
lnum = 1;
@@ -3416,12 +3420,12 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
*/
const size_t dir_len = strlen(*dirp) + 1;
dir_name = xmalloc(dir_len);
- (void)copy_option_part((char_u **)dirp, (char_u *)dir_name, dir_len, ",");
+ (void)copy_option_part(dirp, dir_name, dir_len, ",");
/*
* we try different names until we find one that does not exist yet
*/
- fname = (char *)makeswapname((char_u *)buf_fname, buf->b_ffname, buf,
+ fname = (char *)makeswapname((char_u *)buf_fname, (char_u *)buf->b_ffname, buf,
(char_u *)dir_name);
for (;;) {
@@ -3469,12 +3473,12 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
if (b0.b0_flags & B0_SAME_DIR) {
if (FNAMECMP(path_tail((char *)buf->b_ffname),
path_tail((char *)b0.b0_fname)) != 0
- || !same_directory((char_u *)fname, buf->b_ffname)) {
+ || !same_directory((char_u *)fname, (char_u *)buf->b_ffname)) {
// Symlinks may point to the same file even
// when the name differs, need to check the
// inode too.
expand_env(b0.b0_fname, NameBuff, MAXPATHL);
- if (fnamecmp_ino(buf->b_ffname, NameBuff,
+ if (fnamecmp_ino((char_u *)buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino))) {
differ = TRUE;
}
@@ -3483,7 +3487,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// The name in the swap file may be
// "~user/path/file". Expand it first.
expand_env(b0.b0_fname, NameBuff, MAXPATHL);
- if (fnamecmp_ino(buf->b_ffname, NameBuff,
+ if (fnamecmp_ino((char_u *)buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino))) {
differ = TRUE;
}
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 3bb33f5c48..4d5cf047f9 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -687,6 +687,7 @@ void free_all_mem(void)
// Clear menus.
do_cmdline_cmd("aunmenu *");
+ do_cmdline_cmd("tlunmenu *");
do_cmdline_cmd("menutranslate clear");
// Clear mappings, abbreviations, breakpoints.
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 80f8406ab0..018c62d604 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -11,6 +11,7 @@
#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/eval.h"
@@ -22,6 +23,7 @@
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
+#include "nvim/popupmnu.h"
#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/strings.h"
@@ -36,10 +38,9 @@
#endif
/// The character for each menu mode
-static char menu_mode_chars[] = { 'n', 'v', 's', 'o', 'i', 'c', 't' };
+static char *menu_mode_chars[] = { "n", "v", "s", "o", "i", "c", "tl", "t" };
static char e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu");
-static char e_othermode[] = N_("E328: Menu only exists in another mode");
static char e_nomenu[] = N_("E329: No menu \"%s\"");
// Return true if "name" is a window toolbar menu name.
@@ -571,7 +572,7 @@ static int remove_menu(vimmenu_T **menup, char *name, int modes, bool silent)
}
} else if (*name != NUL) {
if (!silent) {
- emsg(_(e_othermode));
+ emsg(_(e_menuothermode));
}
return FAIL;
}
@@ -723,7 +724,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
(menu->noremap[bit] & REMAP_NONE) ? 1 : 0);
tv_dict_add_nr(impl, S_LEN("sid"),
(menu->noremap[bit] & REMAP_SCRIPT) ? 1 : 0);
- tv_dict_add_dict(commands, &menu_mode_chars[bit], 1, impl);
+ tv_dict_add_dict(commands, menu_mode_chars[bit], 1, impl);
}
}
} else {
@@ -785,7 +786,7 @@ static vimmenu_T *find_menu(vimmenu_T *menu, char *name, int modes)
emsg(_(e_notsubmenu));
return NULL;
} else if ((menu->modes & modes) == 0x0) {
- emsg(_(e_othermode));
+ emsg(_(e_menuothermode));
return NULL;
} else if (*p == NUL) { // found a full match
return menu;
@@ -859,7 +860,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
for (i = 0; i < depth + 2; i++) {
msg_puts(" ");
}
- msg_putchar(menu_mode_chars[bit]);
+ msg_puts(menu_mode_chars[bit]);
if (menu->noremap[bit] == REMAP_NONE) {
msg_putchar('*');
} else if (menu->noremap[bit] == REMAP_SCRIPT) {
@@ -1213,6 +1214,11 @@ int get_menu_cmd_modes(const char *cmd, bool forceit, int *noremap, int *unmenu)
modes = MENU_INSERT_MODE;
break;
case 't':
+ if (*cmd == 'l') { // tlmenu, tlunmenu, tlnoremenu
+ modes = MENU_TERMINAL_MODE;
+ cmd++;
+ break;
+ }
modes = MENU_TIP_MODE; // tmenu
break;
case 'c': // cmenu
@@ -1259,9 +1265,13 @@ static char *popup_mode_name(char *name, int idx)
size_t len = STRLEN(name);
assert(len >= 4);
- char *p = xstrnsave(name, len + 1);
- memmove(p + 6, p + 5, len - 4);
- p[5] = menu_mode_chars[idx];
+ char *mode_chars = menu_mode_chars[idx];
+ size_t mode_chars_len = strlen(mode_chars);
+ char *p = xstrnsave(name, len + mode_chars_len);
+ memmove(p + 5 + mode_chars_len, p + 5, len - 4);
+ for (size_t i = 0; i < mode_chars_len; i++) {
+ p[5 + i] = menu_mode_chars[idx][i];
+ }
return p;
}
@@ -1355,77 +1365,142 @@ static int menu_is_hidden(char *name)
|| (menu_is_popup(name) && name[5] != NUL);
}
-// Execute "menu". Use by ":emenu" and the window toolbar.
-// "eap" is NULL for the window toolbar.
-static void execute_menu(const exarg_T *eap, vimmenu_T *menu)
- FUNC_ATTR_NONNULL_ARG(2)
+static int get_menu_mode(void)
{
- int idx = -1;
- char *mode;
-
- // Use the Insert mode entry when returning to Insert mode.
- if (((State & MODE_INSERT) || restart_edit) && !current_sctx.sc_sid) {
- mode = "Insert";
- idx = MENU_INDEX_INSERT;
- } else if (State & MODE_CMDLINE) {
- mode = "Command";
- idx = MENU_INDEX_CMDLINE;
- } else if (get_real_state() & MODE_VISUAL) {
- // Detect real visual mode -- if we are really in visual mode we
- // don't need to do any guesswork to figure out what the selection
- // is. Just execute the visual binding for the menu.
- mode = "Visual";
- idx = MENU_INDEX_VISUAL;
- } else if (eap != NULL && eap->addr_count) {
- pos_T tpos;
-
- mode = "Visual";
- idx = MENU_INDEX_VISUAL;
-
- // GEDDES: This is not perfect - but it is a
- // quick way of detecting whether we are doing this from a
- // selection - see if the range matches up with the visual
- // select start and end.
- if ((curbuf->b_visual.vi_start.lnum == eap->line1)
- && (curbuf->b_visual.vi_end.lnum) == eap->line2) {
- // Set it up for visual mode - equivalent to gv.
- VIsual_mode = curbuf->b_visual.vi_mode;
- tpos = curbuf->b_visual.vi_end;
- curwin->w_cursor = curbuf->b_visual.vi_start;
- curwin->w_curswant = curbuf->b_visual.vi_curswant;
- } else {
- // Set it up for line-wise visual mode
- VIsual_mode = 'V';
- curwin->w_cursor.lnum = eap->line1;
- curwin->w_cursor.col = 1;
- tpos.lnum = eap->line2;
- tpos.col = MAXCOL;
- tpos.coladd = 0;
+ if (State & MODE_TERMINAL) {
+ return MENU_INDEX_TERMINAL;
+ }
+ if (VIsual_active) {
+ if (VIsual_select) {
+ return MENU_INDEX_SELECT;
}
+ return MENU_INDEX_VISUAL;
+ }
+ if (State & MODE_INSERT) {
+ return MENU_INDEX_INSERT;
+ }
+ if ((State & MODE_CMDLINE) || State == MODE_ASKMORE || State == MODE_HITRETURN) {
+ return MENU_INDEX_CMDLINE;
+ }
+ if (finish_op) {
+ return MENU_INDEX_OP_PENDING;
+ }
+ if (State & MODE_NORMAL) {
+ return MENU_INDEX_NORMAL;
+ }
+ if (State & MODE_LANGMAP) { // must be a "r" command, like Insert mode
+ return MENU_INDEX_INSERT;
+ }
+ return MENU_INDEX_INVALID;
+}
+
+int get_menu_mode_flag(void)
+{
+ int mode = get_menu_mode();
+
+ if (mode == MENU_INDEX_INVALID) {
+ return 0;
+ }
+ return 1 << mode;
+}
- // Activate visual mode
- VIsual_active = TRUE;
- VIsual_reselect = TRUE;
- check_cursor();
- VIsual = curwin->w_cursor;
- curwin->w_cursor = tpos;
+/// Display the Special "PopUp" menu as a pop-up at the current mouse
+/// position. The "PopUpn" menu is for Normal mode, "PopUpi" for Insert mode,
+/// etc.
+void show_popupmenu(void)
+{
+ int menu_mode = get_menu_mode();
+ if (menu_mode == MENU_INDEX_INVALID) {
+ return;
+ }
+ char *mode = menu_mode_chars[menu_mode];
+ size_t mode_len = strlen(mode);
- check_cursor();
+ apply_autocmds(EVENT_MENUPOPUP, mode, NULL, false, curbuf);
- // Adjust the cursor to make sure it is in the correct pos
- // for exclusive mode
- if (*p_sel == 'e' && gchar_cursor() != NUL) {
- curwin->w_cursor.col++;
+ vimmenu_T *menu;
+
+ for (menu = root_menu; menu != NULL; menu = menu->next) {
+ if (STRNCMP("PopUp", menu->name, 5) == 0 && STRNCMP(menu->name + 5, mode, mode_len) == 0) {
+ break;
}
}
- if (idx == -1 || eap == NULL) {
- mode = "Normal";
+ // Only show a popup when it is defined and has entries
+ if (menu != NULL && menu->children != NULL) {
+ pum_show_popupmenu(menu);
+ }
+}
+
+/// Execute "menu". Use by ":emenu" and the window toolbar.
+/// @param eap NULL for the window toolbar.
+/// @param mode_idx specify a MENU_INDEX_ value, use -1 to depend on the current state
+void execute_menu(const exarg_T *eap, vimmenu_T *menu, int mode_idx)
+ FUNC_ATTR_NONNULL_ARG(2)
+{
+ int idx = mode_idx;
+
+ if (idx < 0) {
+ // Use the Insert mode entry when returning to Insert mode.
+ if (((State & MODE_INSERT) || restart_edit) && !current_sctx.sc_sid) {
+ idx = MENU_INDEX_INSERT;
+ } else if (State & MODE_CMDLINE) {
+ idx = MENU_INDEX_CMDLINE;
+ } else if (State & MODE_TERMINAL) {
+ idx = MENU_INDEX_TERMINAL;
+ } else if (get_real_state() & MODE_VISUAL) {
+ // Detect real visual mode -- if we are really in visual mode we
+ // don't need to do any guesswork to figure out what the selection
+ // is. Just execute the visual binding for the menu.
+ idx = MENU_INDEX_VISUAL;
+ } else if (eap != NULL && eap->addr_count) {
+ pos_T tpos;
+
+ idx = MENU_INDEX_VISUAL;
+
+ // GEDDES: This is not perfect - but it is a
+ // quick way of detecting whether we are doing this from a
+ // selection - see if the range matches up with the visual
+ // select start and end.
+ if ((curbuf->b_visual.vi_start.lnum == eap->line1)
+ && (curbuf->b_visual.vi_end.lnum) == eap->line2) {
+ // Set it up for visual mode - equivalent to gv.
+ VIsual_mode = curbuf->b_visual.vi_mode;
+ tpos = curbuf->b_visual.vi_end;
+ curwin->w_cursor = curbuf->b_visual.vi_start;
+ curwin->w_curswant = curbuf->b_visual.vi_curswant;
+ } else {
+ // Set it up for line-wise visual mode
+ VIsual_mode = 'V';
+ curwin->w_cursor.lnum = eap->line1;
+ curwin->w_cursor.col = 1;
+ tpos.lnum = eap->line2;
+ tpos.col = MAXCOL;
+ tpos.coladd = 0;
+ }
+
+ // Activate visual mode
+ VIsual_active = true;
+ VIsual_reselect = true;
+ check_cursor();
+ VIsual = curwin->w_cursor;
+ curwin->w_cursor = tpos;
+
+ check_cursor();
+
+ // Adjust the cursor to make sure it is in the correct pos
+ // for exclusive mode
+ if (*p_sel == 'e' && gchar_cursor() != NUL) {
+ curwin->w_cursor.col++;
+ }
+ }
+ }
+
+ if (idx == MENU_INDEX_INVALID || eap == NULL) {
idx = MENU_INDEX_NORMAL;
}
- assert(idx != MENU_INDEX_INVALID);
- if (menu->strings[idx] != NULL) {
+ if (menu->strings[idx] != NULL && (menu->modes & (1 << idx))) {
// When executing a script or function execute the commands right now.
// Also for the window toolbar
// Otherwise put them in the typeahead buffer.
@@ -1444,6 +1519,30 @@ static void execute_menu(const exarg_T *eap, vimmenu_T *menu)
menu->silent[idx]);
}
} else if (eap != NULL) {
+ char *mode;
+ switch (idx) {
+ case MENU_INDEX_VISUAL:
+ mode = "Visual";
+ break;
+ case MENU_INDEX_SELECT:
+ mode = "Select";
+ break;
+ case MENU_INDEX_OP_PENDING:
+ mode = "Op-pending";
+ break;
+ case MENU_INDEX_TERMINAL:
+ mode = "Terminal";
+ break;
+ case MENU_INDEX_INSERT:
+ mode = "Insert";
+ break;
+ case MENU_INDEX_CMDLINE:
+ mode = "Cmdline";
+ break;
+ // case MENU_INDEX_TIP: cannot happen
+ default:
+ mode = "Normal";
+ }
semsg(_("E335: Menu not defined for %s mode"), mode);
}
}
@@ -1452,9 +1551,43 @@ static void execute_menu(const exarg_T *eap, vimmenu_T *menu)
// execute it.
void ex_emenu(exarg_T *eap)
{
- char *saved_name = xstrdup(eap->arg);
+ char *arg = eap->arg;
+ int mode_idx = -1;
+
+ if (arg[0] && ascii_iswhite(arg[1])) {
+ switch (arg[0]) {
+ case 'n':
+ mode_idx = MENU_INDEX_NORMAL;
+ break;
+ case 'v':
+ mode_idx = MENU_INDEX_VISUAL;
+ break;
+ case 's':
+ mode_idx = MENU_INDEX_SELECT;
+ break;
+ case 'o':
+ mode_idx = MENU_INDEX_OP_PENDING;
+ break;
+ case 't':
+ mode_idx = MENU_INDEX_TERMINAL;
+ break;
+ case 'i':
+ mode_idx = MENU_INDEX_INSERT;
+ break;
+ case 'c':
+ mode_idx = MENU_INDEX_CMDLINE;
+ break;
+ default:
+ semsg(_(e_invarg2), arg);
+ return;
+ }
+ arg = skipwhite(arg + 2);
+ }
+
+ char *saved_name = xstrdup(arg);
vimmenu_T *menu = *get_root_menu(saved_name);
char *name = saved_name;
+ bool gave_emsg = false;
while (*name) {
// Find in the menu hierarchy
char *p = menu_name_skip(name);
@@ -1463,6 +1596,7 @@ void ex_emenu(exarg_T *eap)
if (menu_name_equal(name, menu)) {
if (*p == NUL && menu->children != NULL) {
emsg(_("E333: Menu path must lead to a menu item"));
+ gave_emsg = true;
menu = NULL;
} else if (*p != NUL && menu->children == NULL) {
emsg(_(e_notsubmenu));
@@ -1480,12 +1614,60 @@ void ex_emenu(exarg_T *eap)
}
xfree(saved_name);
if (menu == NULL) {
- semsg(_("E334: Menu not found: %s"), eap->arg);
+ if (!gave_emsg) {
+ semsg(_("E334: Menu not found: %s"), arg);
+ }
return;
}
// Found the menu, so execute.
- execute_menu(eap, menu);
+ execute_menu(eap, menu, mode_idx);
+}
+
+/// Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy.
+vimmenu_T *menu_find(const char *path_name)
+{
+ vimmenu_T *menu = *get_root_menu(path_name);
+ char *saved_name = xstrdup(path_name);
+ char *name = saved_name;
+ while (*name) {
+ // find the end of one dot-separated name and put a NUL at the dot
+ char *p = menu_name_skip(name);
+
+ while (menu != NULL) {
+ if (menu_name_equal(name, menu)) {
+ if (menu->children == NULL) {
+ // found a menu item instead of a sub-menu
+ if (*p == NUL) {
+ emsg(_("E336: Menu path must lead to a sub-menu"));
+ } else {
+ emsg(_(e_notsubmenu));
+ }
+ menu = NULL;
+ goto theend;
+ }
+ if (*p == NUL) { // found a full match
+ goto theend;
+ }
+ break;
+ }
+ menu = menu->next;
+ }
+ if (menu == NULL) { // didn't find it
+ break;
+ }
+
+ // Found a match, search the sub-menu.
+ menu = menu->children;
+ name = p;
+ }
+
+ if (menu == NULL) {
+ emsg(_("E337: Menu not found - check menu names"));
+ }
+theend:
+ xfree(saved_name);
+ return menu;
}
/*
diff --git a/src/nvim/menu.h b/src/nvim/menu.h
index 5c65918d79..9a60ebfb83 100644
--- a/src/nvim/menu.h
+++ b/src/nvim/menu.h
@@ -18,6 +18,7 @@
#define MENU_OP_PENDING_MODE (1 << MENU_INDEX_OP_PENDING)
#define MENU_INSERT_MODE (1 << MENU_INDEX_INSERT)
#define MENU_CMDLINE_MODE (1 << MENU_INDEX_CMDLINE)
+#define MENU_TERMINAL_MODE (1 << MENU_INDEX_TERMINAL)
#define MENU_TIP_MODE (1 << MENU_INDEX_TIP)
#define MENU_ALL_MODES ((1 << MENU_INDEX_TIP) - 1)
/// @}
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 733b4808fe..2c96613bb3 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -387,7 +387,7 @@ char_u *msg_strtrunc(char_u *s, int force)
// composing chars)
len = (room + 2) * 18;
buf = xmalloc((size_t)len);
- trunc_string(s, buf, room, len);
+ trunc_string((char *)s, (char *)buf, room, len);
}
}
return buf;
@@ -395,7 +395,7 @@ char_u *msg_strtrunc(char_u *s, int force)
/// Truncate a string "s" to "buf" with cell width "room".
/// "s" and "buf" may be equal.
-void trunc_string(char_u *s, char_u *buf, int room_in, int buflen)
+void trunc_string(char *s, char *buf, int room_in, int buflen)
{
int room = room_in - 3; // "..." takes 3 chars
int half;
@@ -423,13 +423,13 @@ void trunc_string(char_u *s, char_u *buf, int room_in, int buflen)
buf[e] = NUL;
return;
}
- n = ptr2cells((char *)s + e);
+ n = ptr2cells(s + e);
if (len + n > half) {
break;
}
len += n;
buf[e] = s[e];
- for (n = utfc_ptr2len((char *)s + e); --n > 0;) {
+ for (n = utfc_ptr2len(s + e); --n > 0;) {
if (++e == buflen) {
break;
}
@@ -441,9 +441,9 @@ void trunc_string(char_u *s, char_u *buf, int room_in, int buflen)
half = i = (int)STRLEN(s);
for (;;) {
do {
- half = half - utf_head_off(s, s + half - 1) - 1;
- } while (half > 0 && utf_iscomposing(utf_ptr2char((char *)s + half)));
- n = ptr2cells((char *)s + half);
+ half = half - utf_head_off((char_u *)s, (char_u *)s + half - 1) - 1;
+ } while (half > 0 && utf_iscomposing(utf_ptr2char(s + half)));
+ n = ptr2cells(s + half);
if (len + n > room || half == 0) {
break;
}
@@ -637,7 +637,7 @@ static bool emsg_multiline(const char *s, bool multiline)
return true;
}
- called_emsg = true;
+ called_emsg++;
// If "emsg_severe" is true: When an error exception is to be thrown,
// prefer this message over previous messages for the same command.
@@ -652,7 +652,7 @@ static bool emsg_multiline(const char *s, bool multiline)
// interrupt message).
if (cause_errthrow(s, severe, &ignore)) {
if (!ignore) {
- did_emsg++;
+ did_emsg = true;
}
return true;
}
@@ -717,7 +717,7 @@ static bool emsg_multiline(const char *s, bool multiline)
} else {
flush_buffers(FLUSH_MINIMAL); // flush internal buffers
}
- did_emsg++; // flag for DoOneCmd()
+ did_emsg = true; // flag for DoOneCmd()
}
emsg_on_display = true; // remember there is an error message
@@ -891,7 +891,7 @@ char_u *msg_may_trunc(bool force, char_u *s)
room = (Rows - cmdline_row - 1) * Columns + sc_col - 1;
if ((force || (shortmess(SHM_TRUNC) && !exmode_active))
- && (int)STRLEN(s) - room > 0) {
+ && (int)STRLEN(s) - room > 0 && p_ch > 0) {
int size = vim_strsize((char *)s);
// There may be room anyway when there are multibyte chars.
@@ -1477,10 +1477,8 @@ void msg_home_replace_hl(char_u *fname)
static void msg_home_replace_attr(char_u *fname, int attr)
{
- char_u *name;
-
- name = home_replace_save(NULL, fname);
- msg_outtrans_attr(name, attr);
+ char *name = home_replace_save(NULL, (char *)fname);
+ msg_outtrans_attr((char_u *)name, attr);
xfree(name);
}
@@ -2329,7 +2327,7 @@ bool message_filtered(char_u *msg)
return false;
}
- bool match = vim_regexec(&cmdmod.cmod_filter_regmatch, msg, (colnr_T)0);
+ bool match = vim_regexec(&cmdmod.cmod_filter_regmatch, (char *)msg, (colnr_T)0);
return cmdmod.cmod_filter_force ? match : !match;
}
@@ -3083,10 +3081,11 @@ void msg_clr_eos_force(void)
msg_row = msg_grid_pos;
}
- grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol,
- ' ', ' ', HL_ATTR(HLF_MSG));
if (p_ch > 0) {
- grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns, ' ', ' ', HL_ATTR(HLF_MSG));
+ grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol,
+ ' ', ' ', HL_ATTR(HLF_MSG));
+ grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns,
+ ' ', ' ', HL_ATTR(HLF_MSG));
}
redraw_cmdline = true; // overwritten the command line
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index fc5ecbc6a0..a4a521fa80 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -30,6 +30,49 @@
static linenr_T orig_topline = 0;
static int orig_topfill = 0;
+/// Translate window coordinates to buffer position without any side effects
+int get_fpos_of_mouse(pos_T *mpos)
+{
+ int grid = mouse_grid;
+ int row = mouse_row;
+ int col = mouse_col;
+
+ if (row < 0 || col < 0) { // check if it makes sense
+ return IN_UNKNOWN;
+ }
+
+ // find the window where the row is in
+ win_T *wp = mouse_find_win(&grid, &row, &col);
+ if (wp == NULL) {
+ return IN_UNKNOWN;
+ }
+
+ // winpos and height may change in win_enter()!
+ if (row + wp->w_winbar_height >= wp->w_height) { // In (or below) status line
+ return IN_STATUS_LINE;
+ }
+ if (col >= wp->w_width) { // In vertical separator line
+ return IN_SEP_LINE;
+ }
+
+ if (wp != curwin) {
+ return IN_UNKNOWN;
+ }
+
+ // compute the position in the buffer line from the posn on the screen
+ if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum)) {
+ return IN_STATUS_LINE; // past bottom
+ }
+
+ mpos->col = vcol2col(wp, mpos->lnum, col);
+
+ if (mpos->col > 0) {
+ mpos->col--;
+ }
+ mpos->coladd = 0;
+ return IN_BUFFER;
+}
+
/// Return true if "c" is a mouse key.
bool is_mouse_key(int c)
{
@@ -85,8 +128,10 @@ bool is_mouse_key(int c)
/// @param which_button MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
int jump_to_mouse(int flags, bool *inclusive, int which_button)
{
- static int on_status_line = 0; // #lines below bottom of window
- static int on_sep_line = 0; // on separator right of window
+ static int status_line_offset = 0; // #lines offset from status line
+ static int sep_line_offset = 0; // #cols offset from sep line
+ static bool on_status_line = false;
+ static bool on_sep_line = false;
static bool on_winbar = false;
static int prev_row = -1;
static int prev_col = -1;
@@ -101,6 +146,7 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button)
int col = mouse_col;
int grid = mouse_grid;
int fdc = 0;
+ bool keep_focus = flags & MOUSE_FOCUS;
mouse_past_bottom = false;
mouse_past_eol = false;
@@ -121,10 +167,10 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button)
retnomove:
// before moving the cursor for a left click which is NOT in a status
// line, stop Visual mode
- if (on_status_line) {
+ if (status_line_offset) {
return IN_STATUS_LINE;
}
- if (on_sep_line) {
+ if (sep_line_offset) {
return IN_SEP_LINE;
}
if (on_winbar) {
@@ -146,49 +192,78 @@ retnomove:
old_curwin = curwin;
old_cursor = curwin->w_cursor;
- if (!(flags & MOUSE_FOCUS)) {
- if (row < 0 || col < 0) { // check if it makes sense
- return IN_UNKNOWN;
+ if (row < 0 || col < 0) { // check if it makes sense
+ return IN_UNKNOWN;
+ }
+
+ // find the window where the row is in
+ wp = mouse_find_win(&grid, &row, &col);
+ if (wp == NULL) {
+ return IN_UNKNOWN;
+ }
+
+ on_status_line = (grid == DEFAULT_GRID_HANDLE && row + wp->w_winbar_height >= wp->w_height)
+ ? row + wp->w_winbar_height - wp->w_height + 1 == 1
+ : false;
+
+ on_winbar = (row == -1)
+ ? wp->w_winbar_height != 0
+ : false;
+
+ on_sep_line = grid == DEFAULT_GRID_HANDLE && col >= wp->w_width
+ ? col - wp->w_width + 1 == 1
+ : false;
+
+ // The rightmost character of the status line might be a vertical
+ // separator character if there is no connecting window to the right.
+ if (on_status_line && on_sep_line) {
+ if (stl_connected(wp)) {
+ on_sep_line = false;
+ } else {
+ on_status_line = false;
}
+ }
- // find the window where the row is in
- wp = mouse_find_win(&grid, &row, &col);
- if (wp == NULL) {
- return IN_UNKNOWN;
+ if (keep_focus) {
+ // If we can't change focus, set the value of row, col and grid back to absolute values
+ // since the values relative to the window are only used when keep_focus is false
+ row = mouse_row;
+ col = mouse_col;
+ grid = mouse_grid;
+ }
+
+ if (!keep_focus) {
+ if (on_winbar) {
+ return IN_OTHER_WIN | MOUSE_WINBAR;
}
+
fdc = win_fdccol_count(wp);
dragwin = NULL;
- if (row == -1) {
- on_winbar = wp->w_winbar_height != 0;
- return IN_OTHER_WIN | (on_winbar ? MOUSE_WINBAR : 0);
- }
- on_winbar = false;
-
// winpos and height may change in win_enter()!
if (grid == DEFAULT_GRID_HANDLE && row + wp->w_winbar_height >= wp->w_height) {
// In (or below) status line
- on_status_line = row + wp->w_winbar_height - wp->w_height + 1;
+ status_line_offset = row + wp->w_winbar_height - wp->w_height + 1;
dragwin = wp;
} else {
- on_status_line = 0;
+ status_line_offset = 0;
}
if (grid == DEFAULT_GRID_HANDLE && col >= wp->w_width) {
// In separator line
- on_sep_line = col - wp->w_width + 1;
+ sep_line_offset = col - wp->w_width + 1;
dragwin = wp;
} else {
- on_sep_line = 0;
+ sep_line_offset = 0;
}
// The rightmost character of the status line might be a vertical
// separator character if there is no connecting window to the right.
- if (on_status_line && on_sep_line) {
+ if (status_line_offset && sep_line_offset) {
if (stl_connected(wp)) {
- on_sep_line = 0;
+ sep_line_offset = 0;
} else {
- on_status_line = 0;
+ status_line_offset = 0;
}
}
@@ -196,8 +271,8 @@ retnomove:
// click, stop Visual mode.
if (VIsual_active
&& (wp->w_buffer != curwin->w_buffer
- || (!on_status_line
- && !on_sep_line
+ || (!status_line_offset
+ && !sep_line_offset
&& (wp->w_p_rl
? col < wp->w_width_inner - fdc
: col >= fdc + (cmdwin_type == 0 && wp == curwin ? 0 : 1))
@@ -208,7 +283,7 @@ retnomove:
if (cmdwin_type != 0 && wp != curwin) {
// A click outside the command-line window: Use modeless
// selection if possible. Allow dragging the status lines.
- on_sep_line = 0;
+ sep_line_offset = 0;
row = 0;
col += wp->w_wincol;
wp = curwin;
@@ -223,7 +298,7 @@ retnomove:
if (curwin != old_curwin) {
set_mouse_topline(curwin);
}
- if (on_status_line) { // In (or below) status line
+ if (status_line_offset) { // In (or below) status line
// Don't use start_arrow() if we're in the same window
if (curwin == old_curwin) {
return IN_STATUS_LINE;
@@ -231,7 +306,7 @@ retnomove:
return IN_STATUS_LINE | CURSOR_MOVED;
}
}
- if (on_sep_line) { // In (or below) status line
+ if (sep_line_offset) { // In (or below) status line
// Don't use start_arrow() if we're in the same window
if (curwin == old_curwin) {
return IN_SEP_LINE;
@@ -241,25 +316,27 @@ retnomove:
}
curwin->w_cursor.lnum = curwin->w_topline;
- } else if (on_status_line) {
+ } else if (status_line_offset) {
if (which_button == MOUSE_LEFT && dragwin != NULL) {
// Drag the status line
count = row - dragwin->w_winrow - dragwin->w_height + 1
- - on_status_line;
+ - status_line_offset;
win_drag_status_line(dragwin, count);
did_drag |= count;
}
return IN_STATUS_LINE; // Cursor didn't move
- } else if (on_sep_line && which_button == MOUSE_LEFT) {
+ } else if (sep_line_offset && which_button == MOUSE_LEFT) {
if (dragwin != NULL) {
// Drag the separator column
count = col - dragwin->w_wincol - dragwin->w_width + 1
- - on_sep_line;
+ - sep_line_offset;
win_drag_vsep_line(dragwin, count);
did_drag |= count;
}
return IN_SEP_LINE; // Cursor didn't move
- } else if (on_winbar) {
+ } else if (on_status_line && which_button == MOUSE_RIGHT) {
+ return IN_STATUS_LINE;
+ } else if (on_winbar && which_button == MOUSE_RIGHT) {
// After a click on the window bar don't start Visual mode.
return IN_OTHER_WIN | MOUSE_WINBAR;
} else {
@@ -645,7 +722,7 @@ bool mouse_scroll_horiz(int dir)
return false;
}
- int step = 6;
+ int step = (int)p_mousescroll_hor;
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
step = curwin->w_width_inner;
}
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 99ca5060cd..bd68ad6f97 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -786,8 +786,7 @@ void curs_columns(win_T *wp, int may_scroll)
} else {
wp->w_wrow = wp->w_height_inner - 1 - wp->w_empty_rows;
}
- } else if (wp->w_p_wrap
- && wp->w_width_inner != 0) {
+ } else if (wp->w_p_wrap && wp->w_width_inner != 0) {
width = textwidth + win_col_off2(wp);
// long line wrapping, adjust wp->w_wrow
@@ -1083,8 +1082,7 @@ bool scrolldown(long line_count, int byfold)
* and move the cursor onto the displayed part of the window.
*/
int wrow = curwin->w_wrow;
- if (curwin->w_p_wrap
- && curwin->w_width_inner != 0) {
+ if (curwin->w_p_wrap && curwin->w_width_inner != 0) {
validate_virtcol();
validate_cheight();
wrow += curwin->w_cline_height - 1 -
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 388fa2584c..de01443313 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -532,8 +532,19 @@ void rpc_close(Channel *channel)
}
}
+static void exit_delay_cb(uv_timer_t *handle)
+{
+ uv_timer_stop(&main_loop.exit_delay_timer);
+ multiqueue_put(main_loop.fast_events, exit_event, 0);
+}
+
static void exit_event(void **argv)
{
+ if (exit_need_delay) {
+ uv_timer_start(&main_loop.exit_delay_timer, exit_delay_cb, 0, 0);
+ return;
+ }
+
if (!exiting) {
os_exit(0);
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index aeb85eba1c..b675abfb7d 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -432,6 +432,18 @@ static int find_command(int cmdchar)
return idx;
}
+/// If currently editing a cmdline or text is locked: beep and give an error
+/// message, return true.
+static bool check_text_locked(oparg_T *oap)
+{
+ if (text_locked()) {
+ clearopbeep(oap);
+ text_locked_msg();
+ return true;
+ }
+ return false;
+}
+
/// Normal state entry point. This is called on:
///
/// - Startup, In this case the function never returns.
@@ -559,6 +571,14 @@ static bool normal_need_additional_char(NormalState *s)
static bool normal_need_redraw_mode_message(NormalState *s)
{
+ // In Visual mode and with "^O" in Insert mode, a short message will be
+ // overwritten by the mode message. Wait a bit, until a key is hit.
+ // In Visual mode, it's more important to keep the Visual area updated
+ // than keeping a message (e.g. from a /pat search).
+ // Only do this if the command was typed, not from a mapping.
+ // Don't wait when emsg_silent is non-zero.
+ // Also wait a bit after an error message, e.g. for "^O:".
+ // Don't redraw the screen, it would remove the message.
return (
// 'showmode' is set and messages can be printed
((p_smd && msg_silent == 0
@@ -892,14 +912,6 @@ static void normal_finish_command(NormalState *s)
// Wait for a moment when a message is displayed that will be overwritten
// by the mode message.
- // In Visual mode and with "^O" in Insert mode, a short message will be
- // overwritten by the mode message. Wait a bit, until a key is hit.
- // In Visual mode, it's more important to keep the Visual area updated
- // than keeping a message (e.g. from a /pat search).
- // Only do this if the command was typed, not from a mapping.
- // Don't wait when emsg_silent is non-zero.
- // Also wait a bit after an error message, e.g. for "^O:".
- // Don't redraw the screen, it would remove the message.
if (normal_need_redraw_mode_message(s)) {
normal_redraw_mode_message(s);
}
@@ -1079,15 +1091,9 @@ static int normal_execute(VimState *state, int key)
goto finish;
}
- if (text_locked() && (nv_cmds[s->idx].cmd_flags & NV_NCW)) {
- // This command is not allowed while editing a cmdline: beep.
- clearopbeep(&s->oa);
- text_locked_msg();
- s->command_finished = true;
- goto finish;
- }
-
- if ((nv_cmds[s->idx].cmd_flags & NV_NCW) && curbuf_locked()) {
+ if ((nv_cmds[s->idx].cmd_flags & NV_NCW)
+ && (check_text_locked(&s->oa) || curbuf_locked())) {
+ // this command is not allowed now
s->command_finished = true;
goto finish;
}
@@ -1493,12 +1499,12 @@ static void call_click_def_func(StlClickDefinition *click_defs, int col, int whi
/// Do the appropriate action for the current mouse click in the current mode.
/// Not used for Command-line mode.
///
-/// Normal Mode:
+/// Normal and Visual Mode:
/// event modi- position visual change action
/// fier cursor window
/// left press - yes end yes
/// left press C yes end yes "^]" (2)
-/// left press S yes end yes "*" (2)
+/// left press S yes end (popup: extend) yes "*" (2)
/// left drag - yes start if moved no
/// left relse - yes start if moved no
/// middle press - yes if not active no put register
@@ -1787,9 +1793,52 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
if (mouse_model_popup()) {
if (which_button == MOUSE_RIGHT
&& !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
- // NOTE: Ignore right button down and drag mouse events. Windows only
- // shows the popup menu on the button up event.
- return false;
+ if (!is_click) {
+ // Ignore right button release events, only shows the popup
+ // menu on the button down event.
+ return false;
+ }
+ jump_flags = 0;
+ if (STRCMP(p_mousem, "popup_setpos") == 0) {
+ // First set the cursor position before showing the popup
+ // menu.
+ if (VIsual_active) {
+ pos_T m_pos;
+ // set MOUSE_MAY_STOP_VIS if we are outside the
+ // selection or the current window (might have false
+ // negative here)
+ if (mouse_row < curwin->w_winrow
+ || mouse_row > (curwin->w_winrow + curwin->w_height)) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ } else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ } else {
+ if ((lt(curwin->w_cursor, VIsual)
+ && (lt(m_pos, curwin->w_cursor) || lt(VIsual, m_pos)))
+ || (lt(VIsual, curwin->w_cursor)
+ && (lt(m_pos, VIsual) || lt(curwin->w_cursor, m_pos)))) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ } else if (VIsual_mode == Ctrl_V) {
+ getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
+ getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
+ if (m_pos.col < leftcol || m_pos.col > rightcol) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ }
+ }
+ }
+ } else {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ }
+ }
+ if (jump_flags) {
+ jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
+ update_curbuf(VIsual_active ? INVERTED : VALID);
+ setcursor();
+ ui_flush(); // Update before showing popup menu
+ }
+ show_popupmenu();
+ got_click = false; // ignore release events
+ return (jump_flags & CURSOR_MOVED) != 0;
}
if (which_button == MOUSE_LEFT
&& (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) {
@@ -2035,8 +2084,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
} else if ((mod_mask & MOD_MASK_SHIFT)) {
// Shift-Mouse click searches for the next occurrence of the word under
// the mouse pointer
- if (State & MODE_INSERT
- || (VIsual_active && VIsual_select)) {
+ if (State & MODE_INSERT || (VIsual_active && VIsual_select)) {
stuffcharReadbuff(Ctrl_O);
}
if (which_button == MOUSE_LEFT) {
@@ -2451,21 +2499,30 @@ static void prep_redo_cmd(cmdarg_T *cap)
/// Note that only the last argument can be a multi-byte char.
void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5)
{
+ prep_redo_num2(regname, num, cmd1, cmd2, 0L, cmd3, cmd4, cmd5);
+}
+
+/// Prepare for redo of any command with extra count after "cmd2".
+void prep_redo_num2(int regname, long num1, int cmd1, int cmd2, long num2, int cmd3, int cmd4,
+ int cmd5)
+{
ResetRedobuff();
if (regname != 0) { // yank from specified buffer
AppendCharToRedobuff('"');
AppendCharToRedobuff(regname);
}
- if (num) {
- AppendNumberToRedobuff(num);
+ if (num1 != 0) {
+ AppendNumberToRedobuff(num1);
}
-
if (cmd1 != NUL) {
AppendCharToRedobuff(cmd1);
}
if (cmd2 != NUL) {
AppendCharToRedobuff(cmd2);
}
+ if (num2 != 0) {
+ AppendNumberToRedobuff(num2);
+ }
if (cmd3 != NUL) {
AppendCharToRedobuff(cmd3);
}
@@ -2494,8 +2551,7 @@ static bool checkclearop(oparg_T *oap)
/// @return true if operator or Visual was active.
static bool checkclearopq(oparg_T *oap)
{
- if (oap->op_type == OP_NOP
- && !VIsual_active) {
+ if (oap->op_type == OP_NOP && !VIsual_active) {
return false;
}
clearopbeep(oap);
@@ -3100,7 +3156,7 @@ bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_
}
break;
}
- if (get_leader_len(get_cursor_line_ptr(), NULL, false, true) > 0) {
+ if (get_leader_len((char *)get_cursor_line_ptr(), NULL, false, true) > 0) {
// Ignore this line, continue at start of next line.
curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
@@ -3340,8 +3396,8 @@ static void nv_mousescroll(cmdarg_T *cap)
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
(void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
} else {
- cap->count1 = 3;
- cap->count0 = 3;
+ cap->count1 = p_mousescroll_vert;
+ cap->count0 = p_mousescroll_vert;
nv_scroll_line(cap);
}
} else {
@@ -3422,6 +3478,108 @@ void scroll_redraw(int up, long count)
redraw_later(curwin, VALID);
}
+/// Get the count specified after a 'z' command.
+/// @return true to process the 'z' command and false to skip it.
+static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg)
+{
+ int nchar = *nchar_arg;
+
+ // "z123{nchar}": edit the count before obtaining {nchar}
+ if (checkclearop(cap->oap)) {
+ return false;
+ }
+ long n = nchar - '0';
+
+ for (;;) {
+ no_mapping++;
+ allow_keys++; // no mapping for nchar, but allow key codes
+ nchar = plain_vgetc();
+ LANGMAP_ADJUST(nchar, true);
+ no_mapping--;
+ allow_keys--;
+ (void)add_to_showcmd(nchar);
+ if (nchar == K_DEL || nchar == K_KDEL) {
+ n /= 10;
+ } else if (ascii_isdigit(nchar)) {
+ n = n * 10 + (nchar - '0');
+ } else if (nchar == CAR) {
+ win_setheight((int)n);
+ break;
+ } else if (nchar == 'l'
+ || nchar == 'h'
+ || nchar == K_LEFT
+ || nchar == K_RIGHT) {
+ cap->count1 = n ? n * cap->count1 : cap->count1;
+ *nchar_arg = nchar;
+ return true;
+ } else {
+ clearopbeep(cap->oap);
+ break;
+ }
+ }
+ cap->oap->op_type = OP_NOP;
+ return false;
+}
+
+/// "zug" and "zuw": undo "zg" and "zw"
+/// "zg": add good word to word list
+/// "zw": add wrong word to word list
+/// "zG": add good word to temp word list
+/// "zW": add wrong word to temp word list
+static int nv_zg_zw(cmdarg_T *cap, int nchar)
+{
+ bool undo = false;
+
+ if (nchar == 'u') {
+ no_mapping++;
+ allow_keys++; // no mapping for nchar, but allow key codes
+ nchar = plain_vgetc();
+ LANGMAP_ADJUST(nchar, true);
+ no_mapping--;
+ allow_keys--;
+ (void)add_to_showcmd(nchar);
+ if (vim_strchr("gGwW", nchar) == NULL) {
+ clearopbeep(cap->oap);
+ return OK;
+ }
+ undo = true;
+ }
+
+ if (checkclearop(cap->oap)) {
+ return OK;
+ }
+ char_u *ptr = NULL;
+ size_t len;
+ if (VIsual_active && !get_visual_text(cap, &ptr, &len)) {
+ return FAIL;
+ }
+ if (ptr == NULL) {
+ pos_T pos = curwin->w_cursor;
+
+ // Find bad word under the cursor. When 'spell' is
+ // off this fails and find_ident_under_cursor() is
+ // used below.
+ emsg_off++;
+ len = spell_move_to(curwin, FORWARD, true, true, NULL);
+ emsg_off--;
+ if (len != 0 && curwin->w_cursor.col <= pos.col) {
+ ptr = ml_get_pos(&curwin->w_cursor);
+ }
+ curwin->w_cursor = pos;
+ }
+
+ if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
+ return FAIL;
+ }
+ assert(len <= INT_MAX);
+ spell_add_word(ptr, (int)len,
+ nchar == 'w' || nchar == 'W' ? SPELL_ADD_BAD : SPELL_ADD_GOOD,
+ (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1,
+ undo);
+
+ return OK;
+}
+
/// Commands that start with "z".
static void nv_zet(cmdarg_T *cap)
{
@@ -3430,47 +3588,13 @@ static void nv_zet(cmdarg_T *cap)
int nchar = cap->nchar;
long old_fdl = curwin->w_p_fdl;
int old_fen = curwin->w_p_fen;
- bool undo = false;
int l_p_siso = (int)get_sidescrolloff_value(curwin);
- if (ascii_isdigit(nchar)) {
- // "z123{nchar}": edit the count before obtaining {nchar}
- if (checkclearop(cap->oap)) {
- return;
- }
- n = nchar - '0';
- for (;;) {
- no_mapping++;
- allow_keys++; // no mapping for nchar, but allow key codes
- nchar = plain_vgetc();
- LANGMAP_ADJUST(nchar, true);
- no_mapping--;
- allow_keys--;
- (void)add_to_showcmd(nchar);
- if (nchar == K_DEL || nchar == K_KDEL) {
- n /= 10;
- } else if (ascii_isdigit(nchar)) {
- n = n * 10 + (nchar - '0');
- } else if (nchar == CAR) {
- win_setheight(n);
- break;
- } else if (nchar == 'l'
- || nchar == 'h'
- || nchar == K_LEFT
- || nchar == K_RIGHT) {
- cap->count1 = n ? n * cap->count1 : cap->count1;
- goto dozet;
- } else {
- clearopbeep(cap->oap);
- break;
- }
- }
- cap->oap->op_type = OP_NOP;
+ if (ascii_isdigit(nchar) && !nv_z_get_count(cap, &nchar)) {
return;
}
-dozet:
// "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc"
// and "zC" only in Visual mode. "zj" and "zk" are motion
// commands.
@@ -3820,60 +3944,14 @@ dozet:
break;
case 'u': // "zug" and "zuw": undo "zg" and "zw"
- no_mapping++;
- allow_keys++; // no mapping for nchar, but allow key codes
- nchar = plain_vgetc();
- LANGMAP_ADJUST(nchar, true);
- no_mapping--;
- allow_keys--;
- (void)add_to_showcmd(nchar);
- if (vim_strchr("gGwW", nchar) == NULL) {
- clearopbeep(cap->oap);
- break;
- }
- undo = true;
- FALLTHROUGH;
-
case 'g': // "zg": add good word to word list
case 'w': // "zw": add wrong word to word list
case 'G': // "zG": add good word to temp word list
case 'W': // "zW": add wrong word to temp word list
- {
- char_u *ptr = NULL;
- size_t len;
-
- if (checkclearop(cap->oap)) {
- break;
- }
- if (VIsual_active && !get_visual_text(cap, &ptr, &len)) {
- return;
- }
- if (ptr == NULL) {
- pos_T pos = curwin->w_cursor;
-
- // Find bad word under the cursor. When 'spell' is
- // off this fails and find_ident_under_cursor() is
- // used below.
- emsg_off++;
- len = spell_move_to(curwin, FORWARD, true, true, NULL);
- emsg_off--;
- if (len != 0 && curwin->w_cursor.col <= pos.col) {
- ptr = ml_get_pos(&curwin->w_cursor);
- }
- curwin->w_cursor = pos;
- }
-
- if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
+ if (nv_zg_zw(cap, nchar) == FAIL) {
return;
}
- assert(len <= INT_MAX);
- spell_add_word(ptr, (int)len,
- nchar == 'w' || nchar == 'W'
- ? SPELL_ADD_BAD : SPELL_ADD_GOOD,
- (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1,
- undo);
- }
- break;
+ break;
case '=': // "z=": suggestions for a badly spelled word
if (!checkclearop(cap->oap)) {
@@ -4068,6 +4146,69 @@ void do_nv_ident(int c1, int c2)
nv_ident(&ca);
}
+/// 'K' normal-mode command. Get the command to lookup the keyword under the
+/// cursor.
+static size_t nv_K_getcmd(cmdarg_T *cap, char_u *kp, bool kp_help, bool kp_ex, char_u **ptr_arg,
+ size_t n, char *buf, size_t buf_size)
+{
+ if (kp_help) {
+ // in the help buffer
+ STRCPY(buf, "he! ");
+ return n;
+ }
+
+ if (kp_ex) {
+ // 'keywordprg' is an ex command
+ if (cap->count0 != 0) { // Send the count to the ex command.
+ snprintf(buf, buf_size, "%" PRId64, (int64_t)(cap->count0));
+ }
+ STRCAT(buf, kp);
+ STRCAT(buf, " ");
+ return n;
+ }
+
+ char_u *ptr = *ptr_arg;
+
+ // An external command will probably use an argument starting
+ // with "-" as an option. To avoid trouble we skip the "-".
+ while (*ptr == '-' && n > 0) {
+ ptr++;
+ n--;
+ }
+ if (n == 0) {
+ // found dashes only
+ emsg(_(e_noident));
+ xfree(buf);
+ *ptr_arg = ptr;
+ return 0;
+ }
+
+ // When a count is given, turn it into a range. Is this
+ // really what we want?
+ bool isman = (STRCMP(kp, "man") == 0);
+ bool isman_s = (STRCMP(kp, "man -s") == 0);
+ if (cap->count0 != 0 && !(isman || isman_s)) {
+ snprintf(buf, buf_size, ".,.+%" PRId64, (int64_t)(cap->count0 - 1));
+ }
+
+ do_cmdline_cmd("tabnew");
+ STRCAT(buf, "terminal ");
+ if (cap->count0 == 0 && isman_s) {
+ STRCAT(buf, "man");
+ } else {
+ STRCAT(buf, kp);
+ }
+ STRCAT(buf, " ");
+ if (cap->count0 != 0 && (isman || isman_s)) {
+ snprintf(buf + STRLEN(buf), buf_size - STRLEN(buf), "%" PRId64,
+ (int64_t)cap->count0);
+ STRCAT(buf, " ");
+ }
+
+ *ptr_arg = ptr;
+ return n;
+}
+
/// Handle the commands that use the word under the cursor.
/// [g] CTRL-] :ta to current identifier
/// [g] 'K' run program for current identifier
@@ -4147,48 +4288,9 @@ static void nv_ident(cmdarg_T *cap)
break;
case 'K':
- if (kp_help) {
- STRCPY(buf, "he! ");
- } else if (kp_ex) {
- if (cap->count0 != 0) { // Send the count to the ex command.
- snprintf(buf, buf_size, "%" PRId64, (int64_t)(cap->count0));
- }
- STRCAT(buf, kp);
- STRCAT(buf, " ");
- } else {
- // An external command will probably use an argument starting
- // with "-" as an option. To avoid trouble we skip the "-".
- while (*ptr == '-' && n > 0) {
- ptr++;
- n--;
- }
- if (n == 0) {
- emsg(_(e_noident)); // found dashes only
- xfree(buf);
- return;
- }
-
- // When a count is given, turn it into a range. Is this
- // really what we want?
- bool isman = (STRCMP(kp, "man") == 0);
- bool isman_s = (STRCMP(kp, "man -s") == 0);
- if (cap->count0 != 0 && !(isman || isman_s)) {
- snprintf(buf, buf_size, ".,.+%" PRId64, (int64_t)(cap->count0 - 1));
- }
-
- do_cmdline_cmd("tabnew");
- STRCAT(buf, "terminal ");
- if (cap->count0 == 0 && isman_s) {
- STRCAT(buf, "man");
- } else {
- STRCAT(buf, kp);
- }
- STRCAT(buf, " ");
- if (cap->count0 != 0 && (isman || isman_s)) {
- snprintf(buf + STRLEN(buf), buf_size - STRLEN(buf), "%" PRId64,
- (int64_t)cap->count0);
- STRCAT(buf, " ");
- }
+ n = nv_K_getcmd(cap, kp, kp_help, kp_ex, &ptr, n, buf, buf_size);
+ if (n == 0) {
+ return;
}
break;
@@ -4219,7 +4321,7 @@ static void nv_ident(cmdarg_T *cap)
ptr = vim_strnsave(ptr, n);
if (kp_ex) {
// Escape the argument properly for an Ex command
- p = (char_u *)vim_strsave_fnameescape((const char *)ptr, false);
+ p = (char_u *)vim_strsave_fnameescape((const char *)ptr, VSE_NONE);
} else {
// Escape the argument properly for a shell command
p = vim_strsave_shellescape(ptr, true, true);
@@ -4420,7 +4522,6 @@ static void nv_scroll(cmdarg_T *cap)
static void nv_right(cmdarg_T *cap)
{
long n;
- int PAST_LINE;
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
// <C-Right> and <S-Right> move a word or WORD right
@@ -4433,20 +4534,19 @@ static void nv_right(cmdarg_T *cap)
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
- PAST_LINE = (VIsual_active && *p_sel != 'o');
+ bool past_line = (VIsual_active && *p_sel != 'o');
- // In virtual mode, there's no such thing as "PAST_LINE", as lines are
- // (theoretically) infinitely long.
+ // In virtual edit mode, there's no such thing as "past_line", as lines
+ // are (theoretically) infinitely long.
if (virtual_active()) {
- PAST_LINE = 0;
+ past_line = false;
}
for (n = cap->count1; n > 0; n--) {
- if ((!PAST_LINE && oneright() == false)
- || (PAST_LINE
- && *get_cursor_pos_ptr() == NUL)) {
- // <Space> wraps to next line if 'whichwrap' has 's'.
- // 'l' wraps to next line if 'whichwrap' has 'l'.
+ if ((!past_line && oneright() == false)
+ || (past_line && *get_cursor_pos_ptr() == NUL)) {
+ // <Space> wraps to next line if 'whichwrap' has 's'.
+ // 'l' wraps to next line if 'whichwrap' has 'l'.
// CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
if (((cap->cmdchar == ' ' && vim_strchr((char *)p_ww, 's') != NULL)
|| (cap->cmdchar == 'l' && vim_strchr((char *)p_ww, 'l') != NULL)
@@ -4479,7 +4579,7 @@ static void nv_right(cmdarg_T *cap)
}
}
break;
- } else if (PAST_LINE) {
+ } else if (past_line) {
curwin->w_set_curswant = true;
if (virtual_active()) {
oneright();
@@ -4610,9 +4710,7 @@ static void nv_gotofile(cmdarg_T *cap)
char_u *ptr;
linenr_T lnum = -1;
- if (text_locked()) {
- clearopbeep(cap->oap);
- text_locked_msg();
+ if (check_text_locked(cap->oap)) {
return;
}
if (curbuf_locked()) {
@@ -4795,18 +4893,131 @@ static void nv_csearch(cmdarg_T *cap)
}
}
+/// "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
+/// "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
+/// "[/", "[*", "]/", "]*": go to Nth comment start/end.
+/// "[m" or "]m" search for prev/next start of (Java) method.
+/// "[M" or "]M" search for prev/next end of (Java) method.
+static void nv_bracket_block(cmdarg_T *cap, const pos_T *old_pos)
+{
+ pos_T new_pos = { 0, 0, 0 };
+ pos_T *pos = NULL; // init for GCC
+ pos_T prev_pos;
+ long n;
+ int findc;
+ int c;
+
+ if (cap->nchar == '*') {
+ cap->nchar = '/';
+ }
+ prev_pos.lnum = 0;
+ if (cap->nchar == 'm' || cap->nchar == 'M') {
+ if (cap->cmdchar == '[') {
+ findc = '{';
+ } else {
+ findc = '}';
+ }
+ n = 9999;
+ } else {
+ findc = cap->nchar;
+ n = cap->count1;
+ }
+ for (; n > 0; n--) {
+ if ((pos = findmatchlimit(cap->oap, findc,
+ (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL) {
+ if (new_pos.lnum == 0) { // nothing found
+ if (cap->nchar != 'm' && cap->nchar != 'M') {
+ clearopbeep(cap->oap);
+ }
+ } else {
+ pos = &new_pos; // use last one found
+ }
+ break;
+ }
+ prev_pos = new_pos;
+ curwin->w_cursor = *pos;
+ new_pos = *pos;
+ }
+ curwin->w_cursor = *old_pos;
+
+ // Handle "[m", "]m", "[M" and "[M". The findmatchlimit() only
+ // brought us to the match for "[m" and "]M" when inside a method.
+ // Try finding the '{' or '}' we want to be at.
+ // Also repeat for the given count.
+ if (cap->nchar == 'm' || cap->nchar == 'M') {
+ // norm is true for "]M" and "[m"
+ int norm = ((findc == '{') == (cap->nchar == 'm'));
+
+ n = cap->count1;
+ // found a match: we were inside a method
+ if (prev_pos.lnum != 0) {
+ pos = &prev_pos;
+ curwin->w_cursor = prev_pos;
+ if (norm) {
+ n--;
+ }
+ } else {
+ pos = NULL;
+ }
+ while (n > 0) {
+ for (;;) {
+ if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0) {
+ // if not found anything, that's an error
+ if (pos == NULL) {
+ clearopbeep(cap->oap);
+ }
+ n = 0;
+ break;
+ }
+ c = gchar_cursor();
+ if (c == '{' || c == '}') {
+ // Must have found end/start of class: use it.
+ // Or found the place to be at.
+ if ((c == findc && norm) || (n == 1 && !norm)) {
+ new_pos = curwin->w_cursor;
+ pos = &new_pos;
+ n = 0;
+ } else if (new_pos.lnum == 0) {
+ // if no match found at all, we started outside of the
+ // class and we're inside now. Just go on.
+ new_pos = curwin->w_cursor;
+ pos = &new_pos;
+ } else if ((pos = findmatchlimit(cap->oap, findc,
+ (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
+ 0)) == NULL) {
+ // found start/end of other method: go to match
+ n = 0;
+ } else {
+ curwin->w_cursor = *pos;
+ }
+ break;
+ }
+ }
+ n--;
+ }
+ curwin->w_cursor = *old_pos;
+ if (pos == NULL && new_pos.lnum != 0) {
+ clearopbeep(cap->oap);
+ }
+ }
+ if (pos != NULL) {
+ setpcmark();
+ curwin->w_cursor = *pos;
+ curwin->w_set_curswant = true;
+ if ((fdo_flags & FDO_BLOCK) && KeyTyped
+ && cap->oap->op_type == OP_NOP) {
+ foldOpenCursor();
+ }
+ }
+}
+
/// "[" and "]" commands.
/// cap->arg is BACKWARD for "[" and FORWARD for "]".
static void nv_brackets(cmdarg_T *cap)
{
- pos_T new_pos = { 0, 0, 0 };
- pos_T prev_pos;
- pos_T *pos = NULL; // init for GCC
pos_T old_pos; // cursor position before command
int flag;
long n;
- int findc;
- int c;
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
@@ -4822,7 +5033,7 @@ static void nv_brackets(cmdarg_T *cap)
//
// search list jump
// fwd bwd fwd bwd fwd bwd
- // identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
+ // identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
// define "]d" "[d" "]D" "[D" "]^D" "[^D"
char_u *ptr;
size_t len;
@@ -4855,108 +5066,7 @@ static void nv_brackets(cmdarg_T *cap)
// "[/", "[*", "]/", "]*": go to Nth comment start/end.
// "[m" or "]m" search for prev/next start of (Java) method.
// "[M" or "]M" search for prev/next end of (Java) method.
- if (cap->nchar == '*') {
- cap->nchar = '/';
- }
- prev_pos.lnum = 0;
- if (cap->nchar == 'm' || cap->nchar == 'M') {
- if (cap->cmdchar == '[') {
- findc = '{';
- } else {
- findc = '}';
- }
- n = 9999;
- } else {
- findc = cap->nchar;
- n = cap->count1;
- }
- for (; n > 0; n--) {
- if ((pos = findmatchlimit(cap->oap, findc,
- (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL) {
- if (new_pos.lnum == 0) { // nothing found
- if (cap->nchar != 'm' && cap->nchar != 'M') {
- clearopbeep(cap->oap);
- }
- } else {
- pos = &new_pos; // use last one found
- }
- break;
- }
- prev_pos = new_pos;
- curwin->w_cursor = *pos;
- new_pos = *pos;
- }
- curwin->w_cursor = old_pos;
-
- // Handle "[m", "]m", "[M" and "[M". The findmatchlimit() only
- // brought us to the match for "[m" and "]M" when inside a method.
- // Try finding the '{' or '}' we want to be at.
- // Also repeat for the given count.
- if (cap->nchar == 'm' || cap->nchar == 'M') {
- // norm is true for "]M" and "[m"
- int norm = ((findc == '{') == (cap->nchar == 'm'));
-
- n = cap->count1;
- // found a match: we were inside a method
- if (prev_pos.lnum != 0) {
- pos = &prev_pos;
- curwin->w_cursor = prev_pos;
- if (norm) {
- n--;
- }
- } else {
- pos = NULL;
- }
- while (n > 0) {
- for (;;) {
- if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0) {
- // if not found anything, that's an error
- if (pos == NULL) {
- clearopbeep(cap->oap);
- }
- n = 0;
- break;
- }
- c = gchar_cursor();
- if (c == '{' || c == '}') {
- // Must have found end/start of class: use it.
- // Or found the place to be at.
- if ((c == findc && norm) || (n == 1 && !norm)) {
- new_pos = curwin->w_cursor;
- pos = &new_pos;
- n = 0;
- } else if (new_pos.lnum == 0) {
- // if no match found at all, we started outside of the
- // class and we're inside now. Just go on.
- new_pos = curwin->w_cursor;
- pos = &new_pos;
- } else if ((pos = findmatchlimit(cap->oap, findc,
- (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
- 0)) == NULL) {
- // found start/end of other method: go to match
- n = 0;
- } else {
- curwin->w_cursor = *pos;
- }
- break;
- }
- }
- n--;
- }
- curwin->w_cursor = old_pos;
- if (pos == NULL && new_pos.lnum != 0) {
- clearopbeep(cap->oap);
- }
- }
- if (pos != NULL) {
- setpcmark();
- curwin->w_cursor = *pos;
- curwin->w_set_curswant = true;
- if ((fdo_flags & FDO_BLOCK) && KeyTyped
- && cap->oap->op_type == OP_NOP) {
- foldOpenCursor();
- }
- }
+ nv_bracket_block(cap, &old_pos);
} else if (cap->nchar == '[' || cap->nchar == ']') {
// "[[", "[]", "]]" and "][": move to start or end of function
if (cap->nchar == cap->cmdchar) { // "]]" or "[["
@@ -4983,19 +5093,22 @@ static void nv_brackets(cmdarg_T *cap)
nv_put_opt(cap, true);
} else if (cap->nchar == '\'' || cap->nchar == '`') {
// "['", "[`", "]'" and "]`": jump to next mark
- pos = &curwin->w_cursor;
+ fmark_T *fm = pos_to_mark(curbuf, NULL, curwin->w_cursor);
+ fmark_T *prev_fm;
for (n = cap->count1; n > 0; n--) {
- prev_pos = *pos;
- pos = getnextmark(pos, cap->cmdchar == '[' ? BACKWARD : FORWARD,
- cap->nchar == '\'');
- if (pos == NULL) {
+ prev_fm = fm;
+ fm = getnextmark(&fm->mark, cap->cmdchar == '[' ? BACKWARD : FORWARD,
+ cap->nchar == '\'');
+ if (fm == NULL) {
break;
}
}
- if (pos == NULL) {
- pos = &prev_pos;
+ if (fm == NULL) {
+ fm = prev_fm;
}
- nv_cursormark(cap, cap->nchar == '\'', pos);
+ MarkMove flags = kMarkContext;
+ flags |= cap->nchar == '\'' ? kMarkBeginLine: 0;
+ nv_mark_move_to(cap, flags, fm);
} else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE) {
// [ or ] followed by a middle mouse click: put selected text with
// indent adjustment. Any other button just does as usual.
@@ -5465,31 +5578,28 @@ static void n_swapchar(cmdarg_T *cap)
}
}
-/// Move cursor to mark.
-static void nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos)
-{
- if (check_mark(pos) == false) {
+/// Move the cursor to the mark position
+///
+/// Wrapper to mark_move_to() that also handles normal mode command arguments.
+/// @note It will switch the buffer if neccesarry, move the cursor and set the
+/// view depending on the given flags.
+/// @param cap command line arguments
+/// @param flags for mark_move_to()
+/// @param mark mark
+/// @return The result of calling mark_move_to()
+static MarkMoveRes nv_mark_move_to(cmdarg_T *cap, MarkMove flags, fmark_T *fm)
+{
+ MarkMoveRes res = mark_move_to(fm, flags);
+ if (res & kMarkMoveFailed) {
clearop(cap->oap);
- } else {
- if (cap->cmdchar == '\''
- || cap->cmdchar == '`'
- || cap->cmdchar == '['
- || cap->cmdchar == ']') {
- setpcmark();
- }
- curwin->w_cursor = *pos;
- if (flag) {
- beginline(BL_WHITE | BL_FIX);
- } else {
- check_cursor();
- }
}
- cap->oap->motion_type = flag ? kMTLineWise : kMTCharWise;
+ cap->oap->motion_type = flags & kMarkBeginLine ? kMTLineWise : kMTCharWise;
if (cap->cmdchar == '`') {
cap->oap->use_reg_one = true;
}
cap->oap->inclusive = false; // ignored if not kMTCharWise
curwin->w_set_curswant = true;
+ return res;
}
/// Handle commands that are operators in Visual mode.
@@ -5564,36 +5674,32 @@ static void nv_optrans(cmdarg_T *cap)
/// cap->arg is true for "'" and "g'".
static void nv_gomark(cmdarg_T *cap)
{
- pos_T *pos;
- int c;
- pos_T old_cursor = curwin->w_cursor;
- const bool old_KeyTyped = KeyTyped; // getting file may reset it
+ int name;
+ MarkMove flags = jop_flags & JOP_VIEW ? kMarkSetView : 0; // flags for moving to the mark
+ MarkMoveRes move_res = 0; // Result from moving to the mark
+ const bool old_KeyTyped = KeyTyped; // getting file may reset it
if (cap->cmdchar == 'g') {
- c = cap->extra_char;
+ name = cap->extra_char;
+ flags |= KMarkNoContext;
} else {
- c = cap->nchar;
- }
- pos = getmark(c, (cap->oap->op_type == OP_NOP));
- if (pos == (pos_T *)-1) { // jumped to other file
- if (cap->arg) {
- check_cursor_lnum();
- beginline(BL_WHITE | BL_FIX);
- } else {
- check_cursor();
- }
- } else {
- nv_cursormark(cap, cap->arg, pos);
+ name = cap->nchar;
+ flags |= kMarkContext;
}
+ flags |= cap->arg ? kMarkBeginLine : 0;
+ flags |= cap->count0 ? kMarkSetView : 0;
+
+ fmark_T *fm = mark_get(curbuf, curwin, NULL, kMarkAll, name);
+ move_res = nv_mark_move_to(cap, flags, fm);
// May need to clear the coladd that a mark includes.
if (!virtual_active()) {
curwin->w_cursor.coladd = 0;
}
- check_cursor_col();
+
if (cap->oap->op_type == OP_NOP
- && pos != NULL
- && (pos == (pos_T *)-1 || !equalpos(old_cursor, *pos))
+ && move_res & kMarkMoveSuccess
+ && (move_res & kMarkSwitchedBuf || move_res & kMarkChangedCursor)
&& (fdo_flags & FDO_MARK)
&& old_KeyTyped) {
foldOpenCursor();
@@ -5601,11 +5707,13 @@ static void nv_gomark(cmdarg_T *cap)
}
/// Handle CTRL-O, CTRL-I, "g;", "g,", and "CTRL-Tab" commands.
+/// Movement in the jumplist and changelist.
static void nv_pcmark(cmdarg_T *cap)
{
- pos_T *pos;
- linenr_T lnum = curwin->w_cursor.lnum;
- const bool old_KeyTyped = KeyTyped; // getting file may reset it
+ fmark_T *fm = NULL;
+ MarkMove flags = jop_flags & JOP_VIEW ? kMarkSetView : 0; // flags for moving to the mark
+ MarkMoveRes move_res = 0; // Result from moving to the mark
+ const bool old_KeyTyped = KeyTyped; // getting file may reset it.
if (!checkclearopq(cap->oap)) {
if (cap->cmdchar == TAB && mod_mask == MOD_MASK_CTRL) {
@@ -5614,16 +5722,18 @@ static void nv_pcmark(cmdarg_T *cap)
}
return;
}
+
if (cap->cmdchar == 'g') {
- pos = movechangelist((int)cap->count1);
+ fm = get_changelist(curbuf, curwin, (int)cap->count1);
} else {
- pos = movemark((int)cap->count1);
- }
- if (pos == (pos_T *)-1) { // jump to other file
- curwin->w_set_curswant = true;
- check_cursor();
- } else if (pos != NULL) { // can jump
- nv_cursormark(cap, false, pos);
+ fm = get_jumplist(curwin, (int)cap->count1);
+ flags |= KMarkNoContext | kMarkJumpList;
+ }
+ // Changelist and jumplist have their own error messages. Therefore avoid
+ // calling nv_mark_move_to() when not found to avoid incorrect error
+ // messages.
+ if (fm != NULL) {
+ move_res = nv_mark_move_to(cap, flags, fm);
} else if (cap->cmdchar == 'g') {
if (curbuf->b_changelistlen == 0) {
emsg(_("E664: changelist is empty"));
@@ -5636,7 +5746,7 @@ static void nv_pcmark(cmdarg_T *cap)
clearopbeep(cap->oap);
}
if (cap->oap->op_type == OP_NOP
- && (pos == (pos_T *)-1 || lnum != curwin->w_cursor.lnum)
+ && (move_res & kMarkSwitchedBuf || move_res & kMarkChangedLine)
&& (fdo_flags & FDO_MARK)
&& old_KeyTyped) {
foldOpenCursor();
@@ -5831,13 +5941,217 @@ static void nv_suspend(cmdarg_T *cap)
do_cmdline_cmd("st");
}
+/// "gv": Reselect the previous Visual area. If Visual already active,
+/// exchange previous and current Visual area.
+static void nv_gv_cmd(cmdarg_T *cap)
+{
+ if (checkclearop(cap->oap)) {
+ return;
+ }
+
+ if (curbuf->b_visual.vi_start.lnum == 0
+ || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
+ || curbuf->b_visual.vi_end.lnum == 0) {
+ beep_flush();
+ return;
+ }
+
+ pos_T tpos;
+ // set w_cursor to the start of the Visual area, tpos to the end
+ if (VIsual_active) {
+ int i = VIsual_mode;
+ VIsual_mode = curbuf->b_visual.vi_mode;
+ curbuf->b_visual.vi_mode = i;
+ curbuf->b_visual_mode_eval = i;
+ i = curwin->w_curswant;
+ curwin->w_curswant = curbuf->b_visual.vi_curswant;
+ curbuf->b_visual.vi_curswant = i;
+
+ tpos = curbuf->b_visual.vi_end;
+ curbuf->b_visual.vi_end = curwin->w_cursor;
+ curwin->w_cursor = curbuf->b_visual.vi_start;
+ curbuf->b_visual.vi_start = VIsual;
+ } else {
+ VIsual_mode = curbuf->b_visual.vi_mode;
+ curwin->w_curswant = curbuf->b_visual.vi_curswant;
+ tpos = curbuf->b_visual.vi_end;
+ curwin->w_cursor = curbuf->b_visual.vi_start;
+ }
+
+ VIsual_active = true;
+ VIsual_reselect = true;
+
+ // Set Visual to the start and w_cursor to the end of the Visual
+ // area. Make sure they are on an existing character.
+ check_cursor();
+ VIsual = curwin->w_cursor;
+ curwin->w_cursor = tpos;
+ check_cursor();
+ update_topline(curwin);
+
+ // When called from normal "g" command: start Select mode when
+ // 'selectmode' contains "cmd". When called for K_SELECT, always
+ // start Select mode.
+ if (cap->arg) {
+ VIsual_select = true;
+ VIsual_select_reg = 0;
+ } else {
+ may_start_select('c');
+ }
+ setmouse();
+ redraw_curbuf_later(INVERTED);
+ showmode();
+}
+
+/// "g0", "g^" : Like "0" and "^" but for screen lines.
+/// "gm": middle of "g0" and "g$".
+static void nv_g_home_m_cmd(cmdarg_T *cap)
+{
+ int i;
+ const bool flag = cap->nchar == '^';
+
+ cap->oap->motion_type = kMTCharWise;
+ cap->oap->inclusive = false;
+ if (curwin->w_p_wrap && curwin->w_width_inner != 0) {
+ int width1 = curwin->w_width_inner - curwin_col_off();
+ int width2 = width1 + curwin_col_off2();
+
+ validate_virtcol();
+ i = 0;
+ if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0) {
+ i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
+ }
+ } else {
+ i = curwin->w_leftcol;
+ }
+ // Go to the middle of the screen line. When 'number' or
+ // 'relativenumber' is on and lines are wrapping the middle can be more
+ // to the left.
+ if (cap->nchar == 'm') {
+ i += (curwin->w_width_inner - curwin_col_off()
+ + ((curwin->w_p_wrap && i > 0) ? curwin_col_off2() : 0)) / 2;
+ }
+ coladvance((colnr_T)i);
+ if (flag) {
+ do {
+ i = gchar_cursor();
+ } while (ascii_iswhite(i) && oneright());
+ curwin->w_valid &= ~VALID_WCOL;
+ }
+ curwin->w_set_curswant = true;
+}
+
+/// "g_": to the last non-blank character in the line or <count> lines downward.
+static void nv_g_underscore_cmd(cmdarg_T *cap)
+{
+ cap->oap->motion_type = kMTCharWise;
+ cap->oap->inclusive = true;
+ curwin->w_curswant = MAXCOL;
+ if (cursor_down(cap->count1 - 1, cap->oap->op_type == OP_NOP) == false) {
+ clearopbeep(cap->oap);
+ return;
+ }
+
+ char_u *ptr = get_cursor_line_ptr();
+
+ // In Visual mode we may end up after the line.
+ if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL) {
+ curwin->w_cursor.col--;
+ }
+
+ // Decrease the cursor column until it's on a non-blank.
+ while (curwin->w_cursor.col > 0 && ascii_iswhite(ptr[curwin->w_cursor.col])) {
+ curwin->w_cursor.col--;
+ }
+ curwin->w_set_curswant = true;
+ adjust_for_sel(cap);
+}
+
+/// "g$" : Like "$" but for screen lines.
+static void nv_g_dollar_cmd(cmdarg_T *cap)
+{
+ oparg_T *oap = cap->oap;
+ int i;
+ int col_off = curwin_col_off();
+
+ oap->motion_type = kMTCharWise;
+ oap->inclusive = true;
+ if (curwin->w_p_wrap && curwin->w_width_inner != 0) {
+ curwin->w_curswant = MAXCOL; // so we stay at the end
+ if (cap->count1 == 1) {
+ int width1 = curwin->w_width_inner - col_off;
+ int width2 = width1 + curwin_col_off2();
+
+ validate_virtcol();
+ i = width1 - 1;
+ if (curwin->w_virtcol >= (colnr_T)width1) {
+ i += ((curwin->w_virtcol - width1) / width2 + 1) * width2;
+ }
+ coladvance((colnr_T)i);
+
+ // Make sure we stick in this column.
+ validate_virtcol();
+ curwin->w_curswant = curwin->w_virtcol;
+ curwin->w_set_curswant = false;
+ if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) {
+ // Check for landing on a character that got split at
+ // the end of the line. We do not want to advance to
+ // the next screen line.
+ if (curwin->w_virtcol > (colnr_T)i) {
+ curwin->w_cursor.col--;
+ }
+ }
+ } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) {
+ clearopbeep(oap);
+ }
+ } else {
+ if (cap->count1 > 1) {
+ // if it fails, let the cursor still move to the last char
+ (void)cursor_down(cap->count1 - 1, false);
+ }
+ i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1;
+ coladvance((colnr_T)i);
+
+ // if the character doesn't fit move one back
+ if (curwin->w_cursor.col > 0 && utf_ptr2cells((const char *)get_cursor_pos_ptr()) > 1) {
+ colnr_T vcol;
+
+ getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
+ if (vcol >= curwin->w_leftcol + curwin->w_width - col_off) {
+ curwin->w_cursor.col--;
+ }
+ }
+
+ // Make sure we stick in this column.
+ validate_virtcol();
+ curwin->w_curswant = curwin->w_virtcol;
+ curwin->w_set_curswant = false;
+ }
+}
+
+/// "gi": start Insert at the last position.
+static void nv_gi_cmd(cmdarg_T *cap)
+{
+ if (curbuf->b_last_insert.mark.lnum != 0) {
+ curwin->w_cursor = curbuf->b_last_insert.mark;
+ check_cursor_lnum();
+ int i = (int)STRLEN(get_cursor_line_ptr());
+ if (curwin->w_cursor.col > (colnr_T)i) {
+ if (virtual_active()) {
+ curwin->w_cursor.coladd += curwin->w_cursor.col - i;
+ }
+ curwin->w_cursor.col = i;
+ }
+ }
+ cap->cmdchar = 'i';
+ nv_edit(cap);
+}
+
/// Commands starting with "g".
static void nv_g_cmd(cmdarg_T *cap)
{
oparg_T *oap = cap->oap;
- pos_T tpos;
int i;
- bool flag = false;
switch (cap->nchar) {
// "g^A/g^X": Sequentially increment visually selected region.
@@ -5868,61 +6182,9 @@ static void nv_g_cmd(cmdarg_T *cap)
break;
// "gv": Reselect the previous Visual area. If Visual already active,
- // exchange previous and current Visual area.
+ // exchange previous and current Visual area.
case 'v':
- if (checkclearop(oap)) {
- break;
- }
-
- if (curbuf->b_visual.vi_start.lnum == 0
- || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
- || curbuf->b_visual.vi_end.lnum == 0) {
- beep_flush();
- } else {
- // set w_cursor to the start of the Visual area, tpos to the end
- if (VIsual_active) {
- i = VIsual_mode;
- VIsual_mode = curbuf->b_visual.vi_mode;
- curbuf->b_visual.vi_mode = i;
- curbuf->b_visual_mode_eval = i;
- i = curwin->w_curswant;
- curwin->w_curswant = curbuf->b_visual.vi_curswant;
- curbuf->b_visual.vi_curswant = i;
-
- tpos = curbuf->b_visual.vi_end;
- curbuf->b_visual.vi_end = curwin->w_cursor;
- curwin->w_cursor = curbuf->b_visual.vi_start;
- curbuf->b_visual.vi_start = VIsual;
- } else {
- VIsual_mode = curbuf->b_visual.vi_mode;
- curwin->w_curswant = curbuf->b_visual.vi_curswant;
- tpos = curbuf->b_visual.vi_end;
- curwin->w_cursor = curbuf->b_visual.vi_start;
- }
-
- VIsual_active = true;
- VIsual_reselect = true;
-
- // Set Visual to the start and w_cursor to the end of the Visual
- // area. Make sure they are on an existing character.
- check_cursor();
- VIsual = curwin->w_cursor;
- curwin->w_cursor = tpos;
- check_cursor();
- update_topline(curwin);
- // When called from normal "g" command: start Select mode when
- // 'selectmode' contains "cmd". When called for K_SELECT, always
- // start Select mode.
- if (cap->arg) {
- VIsual_select = true;
- VIsual_select_reg = 0;
- } else {
- may_start_select('c');
- }
- setmouse();
- redraw_curbuf_later(INVERTED);
- showmode();
- }
+ nv_gv_cmd(cap);
break;
// "gV": Don't reselect the previous Visual area after a Select mode mapping of menu.
case 'V':
@@ -5988,47 +6250,14 @@ static void nv_g_cmd(cmdarg_T *cap)
nv_join(cap);
break;
- // "g0", "g^" and "g$": Like "0", "^" and "$" but for screen lines.
+ // "g0", "g^" : Like "0" and "^" but for screen lines.
// "gm": middle of "g0" and "g$".
case '^':
- flag = true;
- FALLTHROUGH;
-
case '0':
case 'm':
case K_HOME:
case K_KHOME:
- oap->motion_type = kMTCharWise;
- oap->inclusive = false;
- if (curwin->w_p_wrap
- && curwin->w_width_inner != 0) {
- int width1 = curwin->w_width_inner - curwin_col_off();
- int width2 = width1 + curwin_col_off2();
-
- validate_virtcol();
- i = 0;
- if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0) {
- i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
- }
- } else {
- i = curwin->w_leftcol;
- }
- // Go to the middle of the screen line. When 'number' or
- // 'relativenumber' is on and lines are wrapping the middle can be more
- // to the left.
- if (cap->nchar == 'm') {
- i += (curwin->w_width_inner - curwin_col_off()
- + ((curwin->w_p_wrap && i > 0)
- ? curwin_col_off2() : 0)) / 2;
- }
- coladvance((colnr_T)i);
- if (flag) {
- do {
- i = gchar_cursor();
- } while (ascii_iswhite(i) && oneright());
- curwin->w_valid &= ~VALID_WCOL;
- }
- curwin->w_set_curswant = true;
+ nv_g_home_m_cmd(cap);
break;
case 'M':
@@ -6043,84 +6272,17 @@ static void nv_g_cmd(cmdarg_T *cap)
curwin->w_set_curswant = true;
break;
+ // "g_": to the last non-blank character in the line or <count> lines downward.
case '_':
- // "g_": to the last non-blank character in the line or <count> lines downward.
- cap->oap->motion_type = kMTCharWise;
- cap->oap->inclusive = true;
- curwin->w_curswant = MAXCOL;
- if (cursor_down(cap->count1 - 1,
- cap->oap->op_type == OP_NOP) == false) {
- clearopbeep(cap->oap);
- } else {
- char_u *ptr = get_cursor_line_ptr();
-
- // In Visual mode we may end up after the line.
- if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL) {
- curwin->w_cursor.col--;
- }
-
- // Decrease the cursor column until it's on a non-blank.
- while (curwin->w_cursor.col > 0
- && ascii_iswhite(ptr[curwin->w_cursor.col])) {
- curwin->w_cursor.col--;
- }
- curwin->w_set_curswant = true;
- adjust_for_sel(cap);
- }
+ nv_g_underscore_cmd(cap);
break;
+ // "g$" : Like "$" but for screen lines.
case '$':
case K_END:
- case K_KEND: {
- int col_off = curwin_col_off();
-
- oap->motion_type = kMTCharWise;
- oap->inclusive = true;
- if (curwin->w_p_wrap
- && curwin->w_width_inner != 0) {
- curwin->w_curswant = MAXCOL; // so we stay at the end
- if (cap->count1 == 1) {
- int width1 = curwin->w_width_inner - col_off;
- int width2 = width1 + curwin_col_off2();
-
- validate_virtcol();
- i = width1 - 1;
- if (curwin->w_virtcol >= (colnr_T)width1) {
- i += ((curwin->w_virtcol - width1) / width2 + 1)
- * width2;
- }
- coladvance((colnr_T)i);
-
- // Make sure we stick in this column.
- validate_virtcol();
- curwin->w_curswant = curwin->w_virtcol;
- curwin->w_set_curswant = false;
- if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) {
- // Check for landing on a character that got split at
- // the end of the line. We do not want to advance to
- // the next screen line.
- if (curwin->w_virtcol > (colnr_T)i) {
- curwin->w_cursor.col--;
- }
- }
- } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) {
- clearopbeep(oap);
- }
- } else {
- if (cap->count1 > 1) {
- // if it fails, let the cursor still move to the last char
- (void)cursor_down(cap->count1 - 1, false);
- }
- i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1;
- coladvance((colnr_T)i);
-
- // Make sure we stick in this column.
- validate_virtcol();
- curwin->w_curswant = curwin->w_virtcol;
- curwin->w_set_curswant = false;
- }
- }
- break;
+ case K_KEND:
+ nv_g_dollar_cmd(cap);
+ break;
// "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
case '*':
@@ -6151,19 +6313,7 @@ static void nv_g_cmd(cmdarg_T *cap)
// "gi": start Insert at the last position.
case 'i':
- if (curbuf->b_last_insert.mark.lnum != 0) {
- curwin->w_cursor = curbuf->b_last_insert.mark;
- check_cursor_lnum();
- i = (int)STRLEN(get_cursor_line_ptr());
- if (curwin->w_cursor.col > (colnr_T)i) {
- if (virtual_active()) {
- curwin->w_cursor.coladd += curwin->w_cursor.col - i;
- }
- curwin->w_cursor.col = i;
- }
- }
- cap->cmdchar = 'i';
- nv_edit(cap);
+ nv_gi_cmd(cap);
break;
// "gI": Start insert in column 1.
@@ -6222,14 +6372,14 @@ static void nv_g_cmd(cmdarg_T *cap)
nv_goto(cap);
break;
- // Two-character operators:
- // "gq" Format text
- // "gw" Format text and keep cursor position
- // "g~" Toggle the case of the text.
- // "gu" Change text to lower case.
- // "gU" Change text to upper case.
- // "g?" rot13 encoding
- // "g@" call 'operatorfunc'
+ // Two-character operators:
+ // "gq" Format text
+ // "gw" Format text and keep cursor position
+ // "g~" Toggle the case of the text.
+ // "gu" Change text to lower case.
+ // "gU" Change text to upper case.
+ // "g?" rot13 encoding
+ // "g@" call 'operatorfunc'
case 'q':
case 'w':
oap->cursor_start = curwin->w_cursor;
@@ -6286,13 +6436,7 @@ static void nv_g_cmd(cmdarg_T *cap)
// "gQ": improved Ex mode
case 'Q':
- if (text_locked()) {
- clearopbeep(cap->oap);
- text_locked_msg();
- break;
- }
-
- if (!checkclearopq(oap)) {
+ if (!check_text_locked(cap->oap) && !checkclearopq(oap)) {
do_exmode();
}
break;
@@ -6410,8 +6554,7 @@ static void nv_redo_or_register(cmdarg_T *cap)
static void nv_Undo(cmdarg_T *cap)
{
// In Visual mode and typing "gUU" triggers an operator
- if (cap->oap->op_type == OP_UPPER
- || VIsual_active) {
+ if (cap->oap->op_type == OP_UPPER || VIsual_active) {
// translate "gUU" to "gUgU"
cap->cmdchar = 'g';
cap->nchar = 'U';
@@ -6426,9 +6569,7 @@ static void nv_Undo(cmdarg_T *cap)
/// single character.
static void nv_tilde(cmdarg_T *cap)
{
- if (!p_to
- && !VIsual_active
- && cap->oap->op_type != OP_TILDE) {
+ if (!p_to && !VIsual_active && cap->oap->op_type != OP_TILDE) {
if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
clearopbeep(cap->oap);
return;
@@ -6804,7 +6945,6 @@ void set_cursor_for_append_to_line(void)
curwin->w_set_curswant = true;
if (get_ve_flags() == VE_ALL) {
const int save_State = State;
-
// Pretend Insert mode here to allow the cursor on the
// character past the end of the line
State = MODE_INSERT;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 42beb2e16d..21ab26898e 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1347,7 +1347,7 @@ bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg)
return true;
case '#': // alternate file name
- *argp = getaltfname(errmsg); // may give emsg if not set
+ *argp = (char_u *)getaltfname(errmsg); // may give emsg if not set
return true;
case '=': // result of expression
@@ -1810,10 +1810,8 @@ setmarks:
/// Used for deletion.
static void mb_adjust_opend(oparg_T *oap)
{
- char_u *p;
-
if (oap->inclusive) {
- p = ml_get(oap->end.lnum);
+ char *p = (char *)ml_get(oap->end.lnum);
oap->end.col += mb_tail_off(p, p + oap->end.col);
}
}
@@ -2791,7 +2789,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
xfree(reg->y_array);
}
- if (message) { // Display message about yank?
+ if (message && (p_ch > 0 || ui_has(kUIMessages))) { // Display message about yank?
if (yank_type == kMTCharWise && yanklines == 1) {
yanklines = 0;
}
@@ -3886,12 +3884,12 @@ void ex_display(exarg_T *eap)
// display alternate file name
if ((arg == NULL || vim_strchr((char *)arg, '%') != NULL) && !got_int) {
- char_u *fname;
+ char *fname;
linenr_T dummy;
- if (buflist_name_nr(0, &fname, &dummy) != FAIL && !message_filtered(fname)) {
+ if (buflist_name_nr(0, &fname, &dummy) != FAIL && !message_filtered((char_u *)fname)) {
msg_puts("\n c \"# ");
- dis_msg(fname, false);
+ dis_msg((char_u *)fname, false);
}
}
@@ -3950,7 +3948,7 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
{
char_u *comment_flags = NULL;
int lead_len;
- int leader_offset = get_last_leader_offset(line, &comment_flags);
+ int leader_offset = get_last_leader_offset((char *)line, (char **)&comment_flags);
*is_comment = false;
if (leader_offset != -1) {
@@ -3972,7 +3970,7 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
return line;
}
- lead_len = get_leader_len(line, &comment_flags, false, include_space);
+ lead_len = get_leader_len((char *)line, (char **)&comment_flags, false, include_space);
if (lead_len == 0) {
return line;
@@ -4423,6 +4421,7 @@ void format_lines(linenr_T line_count, int avoid_fex)
int smd_save;
long count;
bool need_set_indent = true; // set indent of next paragraph
+ linenr_T first_line = curwin->w_cursor.lnum;
bool force_format = false;
const int old_State = State;
@@ -4549,9 +4548,24 @@ void format_lines(linenr_T line_count, int avoid_fex)
*/
if (is_end_par || force_format) {
if (need_set_indent) {
- // replace indent in first line with minimal number of
- // tabs and spaces, according to current options
- (void)set_indent(get_indent(), SIN_CHANGED);
+ int indent = 0; // amount of indent needed
+
+ // Replace indent in first line of a paragraph with minimal
+ // number of tabs and spaces, according to current options.
+ // For the very first formatted line keep the current
+ // indent.
+ if (curwin->w_cursor.lnum == first_line) {
+ indent = get_indent();
+ } else if (curbuf->b_p_lisp) {
+ indent = get_lisp_indent();
+ } else {
+ if (cindent_on()) {
+ indent = *curbuf->b_p_inde != NUL ? get_expr_indent() : get_c_indent();
+ } else {
+ indent = get_indent();
+ }
+ }
+ (void)set_indent(indent, SIN_CHANGED);
}
// put cursor on last non-space
@@ -4654,7 +4668,7 @@ static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags,
ptr = ml_get(lnum);
if (do_comments) {
- *leader_len = get_leader_len(ptr, leader_flags, false, true);
+ *leader_len = get_leader_len((char *)ptr, (char **)leader_flags, false, true);
} else {
*leader_len = 0;
}
@@ -5597,7 +5611,7 @@ void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_a
semsg(_(e_nobufnr), (int64_t)num);
}
} else {
- buf = buflist_findnr(buflist_findpat(str, str + STRLEN(str),
+ buf = buflist_findnr(buflist_findpat((char *)str, (char *)str + STRLEN(str),
true, false, false));
}
if (buf == NULL) {
@@ -5993,9 +6007,9 @@ void cursor_pos_info(dict_T *dict)
} else {
p = get_cursor_line_ptr();
validate_virtcol();
- col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
+ col_print((char *)buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
(int)curwin->w_virtcol + 1);
- col_print(buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p));
+ col_print((char *)buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p));
if (char_count_cursor == byte_count_cursor
&& char_count == byte_count) {
@@ -6220,6 +6234,15 @@ static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial)
oap->start = curwin->w_cursor;
}
+/// Information for redoing the previous Visual selection.
+typedef struct {
+ int rv_mode; ///< 'v', 'V', or Ctrl-V
+ linenr_T rv_line_count; ///< number of lines
+ colnr_T rv_vcol; ///< number of cols or end column
+ long rv_count; ///< count for Visual operator
+ int rv_arg; ///< extra argument
+} redo_VIsual_T;
+
/// Handle an operator after Visual mode or when the movement is finished.
/// "gui_yank" is true when yanking text for the clipboard.
void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
@@ -6231,11 +6254,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
int lbr_saved = curwin->w_p_lbr;
// The visual area is remembered for redo
- static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V
- static linenr_T redo_VIsual_line_count; // number of lines
- static colnr_T redo_VIsual_vcol; // number of cols or end column
- static long redo_VIsual_count; // count for Visual operator
- static int redo_VIsual_arg; // extra argument
+ static redo_VIsual_T redo_VIsual = { NUL, 0, 0, 0, 0 };
+
bool include_line_break = false;
old_cursor = curwin->w_cursor;
@@ -6318,28 +6338,27 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
if (redo_VIsual_busy) {
// Redo of an operation on a Visual area. Use the same size from
- // redo_VIsual_line_count and redo_VIsual_vcol.
+ // redo_VIsual.rv_line_count and redo_VIsual.rv_vcol.
oap->start = curwin->w_cursor;
- curwin->w_cursor.lnum += redo_VIsual_line_count - 1;
+ curwin->w_cursor.lnum += redo_VIsual.rv_line_count - 1;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
- VIsual_mode = redo_VIsual_mode;
- if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') {
+ VIsual_mode = redo_VIsual.rv_mode;
+ if (redo_VIsual.rv_vcol == MAXCOL || VIsual_mode == 'v') {
if (VIsual_mode == 'v') {
- if (redo_VIsual_line_count <= 1) {
+ if (redo_VIsual.rv_line_count <= 1) {
validate_virtcol();
- curwin->w_curswant =
- curwin->w_virtcol + redo_VIsual_vcol - 1;
+ curwin->w_curswant = curwin->w_virtcol + redo_VIsual.rv_vcol - 1;
} else {
- curwin->w_curswant = redo_VIsual_vcol;
+ curwin->w_curswant = redo_VIsual.rv_vcol;
}
} else {
curwin->w_curswant = MAXCOL;
}
coladvance(curwin->w_curswant);
}
- cap->count0 = redo_VIsual_count;
+ cap->count0 = redo_VIsual.rv_count;
cap->count1 = (cap->count0 == 0 ? 1 : cap->count0);
} else if (VIsual_active) {
if (!gui_yank) {
@@ -6426,7 +6445,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
virtual_op = virtual_active();
if (VIsual_active || redo_VIsual_busy) {
- get_op_vcol(oap, redo_VIsual_vcol, true);
+ get_op_vcol(oap, redo_VIsual.rv_vcol, true);
if (!redo_VIsual_busy && !gui_yank) {
// Prepare to reselect and redo Visual: this is based on the
@@ -6471,6 +6490,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
get_op_char(oap->op_type), get_extra_op_char(oap->op_type),
oap->motion_force, cap->cmdchar, cap->nchar);
} else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) {
+ int opchar = get_op_char(oap->op_type);
+ int extra_opchar = get_extra_op_char(oap->op_type);
int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL;
// reverse what nv_replace() did
@@ -6479,15 +6500,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
} else if (nchar == REPLACE_NL_NCHAR) {
nchar = NL;
}
- prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type),
- get_extra_op_char(oap->op_type), nchar);
+
+ if (opchar == 'g' && extra_opchar == '@') {
+ // also repeat the count for 'operatorfunc'
+ prep_redo_num2(oap->regname, 0L, NUL, 'v', cap->count0, opchar, extra_opchar, nchar);
+ } else {
+ prep_redo(oap->regname, 0L, NUL, 'v', opchar, extra_opchar, nchar);
+ }
}
if (!redo_VIsual_busy) {
- redo_VIsual_mode = resel_VIsual_mode;
- redo_VIsual_vcol = resel_VIsual_vcol;
- redo_VIsual_line_count = resel_VIsual_line_count;
- redo_VIsual_count = cap->count0;
- redo_VIsual_arg = cap->arg;
+ redo_VIsual.rv_mode = resel_VIsual_mode;
+ redo_VIsual.rv_vcol = resel_VIsual_vcol;
+ redo_VIsual.rv_line_count = resel_VIsual_line_count;
+ redo_VIsual.rv_count = cap->count0;
+ redo_VIsual.rv_arg = cap->arg;
}
}
@@ -6601,9 +6627,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
switch (oap->op_type) {
case OP_LSHIFT:
case OP_RSHIFT:
- op_shift(oap, true,
- oap->is_VIsual ? (int)cap->count1 :
- 1);
+ op_shift(oap, true, oap->is_VIsual ? (int)cap->count1 : 1);
auto_format(false, true);
break;
@@ -6737,12 +6761,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
op_format(oap, true); // use internal function
break;
- case OP_FUNCTION:
+ case OP_FUNCTION: {
+ redo_VIsual_T save_redo_VIsual = redo_VIsual;
+
// Restore linebreak, so that when the user edits it looks as
// before.
curwin->w_p_lbr = lbr_saved;
- op_function(oap); // call 'operatorfunc'
+ // call 'operatorfunc'
+ op_function(oap);
+
+ // Restore the info for redoing Visual mode, the function may
+ // invoke another operator and unintentionally change it.
+ redo_VIsual = save_redo_VIsual;
break;
+ }
case OP_INSERT:
case OP_APPEND:
@@ -6823,7 +6855,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
} else {
VIsual_active = true;
curwin->w_p_lbr = lbr_saved;
- op_addsub(oap, (linenr_T)cap->count1, redo_VIsual_arg);
+ op_addsub(oap, (linenr_T)cap->count1, redo_VIsual.rv_arg);
VIsual_active = false;
}
check_cursor_col();
@@ -6961,7 +6993,7 @@ bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines)
return false;
}
const char *p = regtype.data + 1;
- reg->y_width = getdigits_int((char_u **)&p, false, 1) - 1;
+ reg->y_width = getdigits_int((char **)&p, false, 1) - 1;
if (regtype.size > (size_t)(p - regtype.data)) {
return false;
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 09a597f7e2..821c7208e3 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1400,7 +1400,7 @@ int do_set(char *arg, int opt_flags)
*/
else if (varp == (char_u *)&p_bs
&& ascii_isdigit(**(char_u **)varp)) {
- i = getdigits_int((char_u **)varp, true, 0);
+ i = getdigits_int((char **)varp, true, 0);
switch (i) {
case 0:
*(char_u **)varp = empty_option;
@@ -1435,7 +1435,7 @@ int do_set(char *arg, int opt_flags)
else if (varp == (char_u *)&p_ww
&& ascii_isdigit(*arg)) {
*errbuf = NUL;
- i = getdigits_int((char_u **)&arg, true, 0);
+ i = getdigits_int(&arg, true, 0);
if (i & 1) {
STRLCAT(errbuf, "b,", sizeof(errbuf));
}
@@ -1728,7 +1728,7 @@ skip:
IObuff[i + ((char_u *)arg - startarg)] = NUL;
}
// make sure all characters are printable
- trans_characters(IObuff, IOSIZE);
+ trans_characters((char *)IObuff, IOSIZE);
no_wait_return++; // wait_return done later
emsg((char *)IObuff); // show error highlighted
@@ -2366,6 +2366,69 @@ static bool valid_spellfile(const char_u *val)
return true;
}
+/// Handle setting 'mousescroll'.
+/// @return error message, NULL if it's OK.
+static char *check_mousescroll(char *string)
+{
+ long vertical = -1;
+ long horizontal = -1;
+
+ for (;;) {
+ char *end = vim_strchr(string, ',');
+ size_t length = end ? (size_t)(end - string) : STRLEN(string);
+
+ // Both "ver:" and "hor:" are 4 bytes long.
+ // They should be followed by at least one digit.
+ if (length <= 4) {
+ return e_invarg;
+ }
+
+ long *direction;
+
+ if (memcmp(string, "ver:", 4) == 0) {
+ direction = &vertical;
+ } else if (memcmp(string, "hor:", 4) == 0) {
+ direction = &horizontal;
+ } else {
+ return e_invarg;
+ }
+
+ // If the direction has already been set, this is a duplicate.
+ if (*direction != -1) {
+ return e_invarg;
+ }
+
+ // Verify that only digits follow the colon.
+ for (size_t i = 4; i < length; i++) {
+ if (!ascii_isdigit(string[i])) {
+ return N_("E548: digit expected");
+ }
+ }
+
+ string += 4;
+ *direction = getdigits_int(&string, false, -1);
+
+ // Num options are generally kept within the signed int range.
+ // We know this number won't be negative because we've already checked for
+ // a minus sign. We'll allow 0 as a means of disabling mouse scrolling.
+ if (*direction == -1) {
+ return e_invarg;
+ }
+
+ if (!end) {
+ break;
+ }
+
+ string = end + 1;
+ }
+
+ // If a direction wasn't set, fallback to the default value.
+ p_mousescroll_vert = (vertical == -1) ? MOUSESCROLL_VERT_DFLT : vertical;
+ p_mousescroll_hor = (horizontal == -1) ? MOUSESCROLL_HOR_DFLT : horizontal;
+
+ return NULL;
+}
+
/// Handle string options that need some action to perform when changed.
/// Returns NULL for success, or an error message for an error.
///
@@ -2859,6 +2922,8 @@ ambw_end:
if (check_opt_strings(p_mousem, p_mousem_values, false) != OK) {
errmsg = e_invarg;
}
+ } else if (varp == &p_mousescroll) { // 'mousescroll'
+ errmsg = check_mousescroll((char *)p_mousescroll);
} else if (varp == &p_swb) { // 'switchbuf'
if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
errmsg = e_invarg;
@@ -2941,7 +3006,7 @@ ambw_end:
if (*++s == '-') { // ignore a '-'
s++;
}
- wid = getdigits_int(&s, true, 0);
+ wid = getdigits_int((char **)&s, true, 0);
if (wid && *s == '(' && (errmsg = check_stl_option((char *)p_ruf)) == NULL) {
ru_wid = wid;
} else {
@@ -3457,7 +3522,7 @@ char *check_colorcolumn(win_T *wp)
if (!ascii_isdigit(*s)) {
return e_invarg;
}
- col = col * getdigits_int(&s, true, 0);
+ col = col * getdigits_int((char **)&s, true, 0);
if (wp->w_buffer->b_p_tw == 0) {
goto skip; // 'textwidth' not set, skip this item
}
@@ -3472,7 +3537,7 @@ char *check_colorcolumn(win_T *wp)
goto skip;
}
} else if (ascii_isdigit(*s)) {
- col = getdigits_int(&s, true, 0);
+ col = getdigits_int((char **)&s, true, 0);
} else {
return e_invarg;
}
@@ -4278,7 +4343,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
if (options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
- BOOLEAN_OBJ(value));
+ BOOLEAN_OBJ(*varp));
}
comp_col(); // in case 'ruler' or 'showcmd' changed
@@ -4719,7 +4784,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
if (errmsg == NULL && options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
- INTEGER_OBJ(value));
+ INTEGER_OBJ(*pp));
}
comp_col(); // in case 'columns' or 'ls' changed
@@ -5650,7 +5715,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, uint6
if (fprintf(fd, "%s %s+=", cmd, name) < 0) {
goto fail;
}
- (void)copy_option_part(&p, part, size, ",");
+ (void)copy_option_part((char **)&p, (char *)part, size, ",");
if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) {
goto fail;
}
@@ -6921,7 +6986,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***
if (xp->xp_context != EXPAND_BOOL_SETTINGS) {
for (match = 0; match < (int)ARRAY_SIZE(names);
match++) {
- if (vim_regexec(regmatch, (char_u *)names[match], (colnr_T)0)) {
+ if (vim_regexec(regmatch, names[match], (colnr_T)0)) {
if (loop == 0) {
num_normal++;
} else {
@@ -6940,10 +7005,10 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***
continue;
}
match = false;
- if (vim_regexec(regmatch, str, (colnr_T)0)
+ if (vim_regexec(regmatch, (char *)str, (colnr_T)0)
|| (options[opt_idx].shortname != NULL
&& vim_regexec(regmatch,
- (char_u *)options[opt_idx].shortname,
+ options[opt_idx].shortname,
(colnr_T)0))) {
match = true;
}
@@ -7460,7 +7525,7 @@ void save_file_ff(buf_T *buf)
if (buf->b_start_fenc == NULL
|| STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0) {
xfree(buf->b_start_fenc);
- buf->b_start_fenc = vim_strsave(buf->b_p_fenc);
+ buf->b_start_fenc = (char *)vim_strsave(buf->b_p_fenc);
}
}
@@ -7827,10 +7892,10 @@ static bool briopt_check(win_T *wp)
if (STRNCMP(p, "shift:", 6) == 0
&& ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) {
p += 6;
- bri_shift = getdigits_int(&p, true, 0);
+ bri_shift = getdigits_int((char **)&p, true, 0);
} else if (STRNCMP(p, "min:", 4) == 0 && ascii_isdigit(p[4])) {
p += 4;
- bri_min = getdigits_int(&p, true, 0);
+ bri_min = getdigits_int((char **)&p, true, 0);
} else if (STRNCMP(p, "sbr", 3) == 0) {
p += 3;
bri_sbr = true;
@@ -7991,10 +8056,10 @@ char_u *skip_to_option_part(const char_u *p)
/// @param[in] sep_chars chars that separate the option parts
///
/// @return length of `*option`
-size_t copy_option_part(char_u **option, char_u *buf, size_t maxlen, char *sep_chars)
+size_t copy_option_part(char **option, char *buf, size_t maxlen, char *sep_chars)
{
size_t len = 0;
- char_u *p = *option;
+ char *p = *option;
// skip '.' at start of option part, for 'suffixes'
if (*p == '.') {
@@ -8015,7 +8080,7 @@ size_t copy_option_part(char_u **option, char_u *buf, size_t maxlen, char *sep_c
if (*p != NUL && *p != ',') { // skip non-standard separator
p++;
}
- p = skip_to_option_part(p); // p points to next file name
+ p = (char *)skip_to_option_part((char_u *)p); // p points to next file name
*option = p;
return len;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 9268b1eff6..237288fbad 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -153,6 +153,11 @@
#define MOUSE_NONE ' ' // don't use Visual selection
#define MOUSE_NONEF 'x' // forced modeless selection
+// default vertical and horizontal mouse scroll values.
+// Note: This should be in sync with the default mousescroll option.
+#define MOUSESCROLL_VERT_DFLT 3
+#define MOUSESCROLL_HOR_DFLT 6
+
#define COCU_ALL "nvic" // flags for 'concealcursor'
/// characters for p_shm option:
@@ -491,9 +496,10 @@ EXTERN int p_js; // 'joinspaces'
EXTERN char_u *p_jop; // 'jumpooptions'
EXTERN unsigned jop_flags;
#ifdef IN_OPTION_C
-static char *(p_jop_values[]) = { "stack", NULL };
+static char *(p_jop_values[]) = { "stack", "view", NULL };
#endif
#define JOP_STACK 0x01
+#define JOP_VIEW 0x02
EXTERN char_u *p_kp; // 'keywordprg'
EXTERN char_u *p_km; // 'keymodel'
EXTERN char_u *p_langmap; // 'langmap'
@@ -527,6 +533,9 @@ EXTERN long p_mls; // 'modelines'
EXTERN char_u *p_mouse; // 'mouse'
EXTERN char_u *p_mousem; // 'mousemodel'
EXTERN int p_mousef; // 'mousefocus'
+EXTERN char_u *p_mousescroll; // 'mousescroll'
+EXTERN long p_mousescroll_vert INIT(= MOUSESCROLL_VERT_DFLT);
+EXTERN long p_mousescroll_hor INIT(= MOUSESCROLL_HOR_DFLT);
EXTERN long p_mouset; // 'mousetime'
EXTERN int p_more; // 'more'
EXTERN char_u *p_opfunc; // 'operatorfunc'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index a0fbf8d9f0..9e4a6a084c 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -1597,7 +1597,7 @@ return {
short_desc=N_("the use of mouse clicks"),
type='string', list='flags', scope={'global'},
varname='p_mouse',
- defaults={if_true=""}
+ defaults={if_true="nvi"}
},
{
full_name='mousefocus', abbreviation='mousef',
@@ -1619,7 +1619,15 @@ return {
short_desc=N_("changes meaning of mouse buttons"),
type='string', scope={'global'},
varname='p_mousem',
- defaults={if_true="extend"}
+ defaults={if_true="popup_setpos"}
+ },
+ {
+ full_name='mousescroll',
+ short_desc=N_("amount to scroll by when scrolling with a mouse"),
+ type='string', list='comma', scope={'global'},
+ vi_def=true,
+ varname='p_mousescroll',
+ defaults={if_true="ver:3,hor:6"}
},
{
full_name='mouseshape', abbreviation='mouses',
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 2a7f7a221f..9c93057fe7 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -884,7 +884,7 @@ void vim_get_prefix_from_exepath(char *exe_name)
// TODO(bfredl): param could have been written as "char exe_name[MAXPATHL]"
// but c_grammar.lua does not recognize it (yet).
xstrlcpy(exe_name, get_vim_var_str(VV_PROGPATH), MAXPATHL * sizeof(*exe_name));
- char *path_end = (char *)path_tail_with_sep((char_u *)exe_name);
+ char *path_end = path_tail_with_sep(exe_name);
*path_end = '\0'; // remove the trailing "nvim.exe"
path_end = path_tail(exe_name);
*path_end = '\0'; // remove the trailing "bin/"
@@ -1143,15 +1143,16 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si
/// Like home_replace, store the replaced string in allocated memory.
/// @param buf When not NULL, check for help files
/// @param src Input file name
-char_u *home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET
+char *home_replace_save(buf_T *buf, char *src)
+ FUNC_ATTR_NONNULL_RET
{
size_t len = 3; // space for "~/" and trailing NUL
if (src != NULL) { // just in case
len += STRLEN(src);
}
char *dst = xmalloc(len);
- home_replace(buf, (char *)src, dst, len, true);
- return (char_u *)dst;
+ home_replace(buf, src, dst, len, true);
+ return dst;
}
/// Function given to ExpandGeneric() to obtain an environment variable name.
@@ -1189,7 +1190,7 @@ bool os_setenv_append_path(const char *fname)
internal_error("os_setenv_append_path()");
return false;
}
- const char *tail = (char *)path_tail_with_sep((char_u *)fname);
+ const char *tail = path_tail_with_sep((char *)fname);
size_t dirlen = (size_t)(tail - fname);
assert(tail >= fname && dirlen + 1 < sizeof(os_buf));
xstrlcpy(os_buf, fname, dirlen + 1);
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 7c5e4f31d7..901a1bc5a6 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -935,7 +935,7 @@ int os_mkdir_recurse(const char *const dir, int32_t mode, char **const failed_di
const char *const real_end = e;
const char past_head_save = *past_head;
while (!os_isdir((char_u *)curdir)) {
- e = (char *)path_tail_with_sep((char_u *)curdir);
+ e = path_tail_with_sep(curdir);
if (e <= past_head) {
*past_head = NUL;
break;
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index c6c43aac92..581f025a0f 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -165,8 +165,7 @@ static char *signal_name(int signum)
// This function handles deadly signals.
// It tries to preserve any swap files and exit properly.
// (partly from Elvis).
-// NOTE: Avoid unsafe functions, such as allocating memory, they can result in
-// a deadlock.
+// NOTE: this is scheduled on the event loop, not called directly from a signal handler.
static void deadly_signal(int signum)
FUNC_ATTR_NORETURN
{
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index 5576e7ba07..59d315d44c 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -115,6 +115,10 @@ char *get_xdg_home(const XDGVarType idx)
#else
dir = concat_fnames_realloc(dir, "nvim", true);
#endif
+
+#ifdef BACKSLASH_IN_FILENAME
+ slash_adjust((char_u *)dir);
+#endif
}
return dir;
}
diff --git a/src/nvim/path.c b/src/nvim/path.c
index f5c662ca88..b22c0a18bd 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -125,14 +125,14 @@ char *path_tail(const char *fname)
/// - Pointer to the last path separator of `fname`, if there is any.
/// - `fname` if it contains no path separator.
/// - Never NULL.
-char_u *path_tail_with_sep(char_u *fname)
+char *path_tail_with_sep(char *fname)
{
assert(fname != NULL);
// Don't remove the '/' from "c:/file".
- char_u *past_head = get_past_head(fname);
- char_u *tail = (char_u *)path_tail((char *)fname);
- while (tail > past_head && after_pathsep((char *)fname, (char *)tail)) {
+ char *past_head = (char *)get_past_head((char_u *)fname);
+ char *tail = path_tail(fname);
+ while (tail > past_head && after_pathsep(fname, tail)) {
tail--;
}
return tail;
@@ -326,11 +326,11 @@ void shorten_dir(char_u *str)
*/
bool dir_of_file_exists(char_u *fname)
{
- char_u *p = path_tail_with_sep(fname);
- if (p == fname) {
+ char *p = path_tail_with_sep((char *)fname);
+ if ((char_u *)p == fname) {
return true;
}
- char_u c = *p;
+ char c = *p;
*p = NUL;
bool retval = os_isdir(fname);
*p = c;
@@ -731,7 +731,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
|| ((flags & EW_DODOT)
&& name[1] != NUL
&& (name[1] != '.' || name[2] != NUL))) // -V557
- && ((regmatch.regprog != NULL && vim_regexec(&regmatch, name, 0))
+ && ((regmatch.regprog != NULL && vim_regexec(&regmatch, (char *)name, 0))
|| ((flags & EW_NOTWILD)
&& FNAMENCMP(path + (s - buf), name, e - s) == 0))) {
STRCPY(s, name);
@@ -845,7 +845,7 @@ static void expand_path_option(char_u *curdir, garray_T *gap)
char_u *buf = xmalloc(MAXPATHL);
while (*path_option != NUL) {
- copy_option_part(&path_option, buf, MAXPATHL, " ,");
+ copy_option_part((char **)&path_option, (char *)buf, MAXPATHL, " ,");
if (buf[0] == '.' && (buf[1] == NUL || vim_ispathsep(buf[1]))) {
/* Relative to current buffer:
@@ -854,8 +854,8 @@ static void expand_path_option(char_u *curdir, garray_T *gap)
if (curbuf->b_ffname == NULL) {
continue;
}
- char_u *p = (char_u *)path_tail((char *)curbuf->b_ffname);
- size_t len = (size_t)(p - curbuf->b_ffname);
+ char_u *p = (char_u *)path_tail(curbuf->b_ffname);
+ size_t len = (size_t)(p - (char_u *)curbuf->b_ffname);
if (len + STRLEN(buf) >= MAXPATHL) {
continue;
}
@@ -996,7 +996,7 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
if (pattern[0] == '*' && pattern[1] == '*'
&& vim_ispathsep_nocolon(pattern[2])
&& path_cutoff != NULL
- && vim_regexec(&regmatch, path_cutoff, (colnr_T)0)
+ && vim_regexec(&regmatch, (char *)path_cutoff, (colnr_T)0)
&& is_unique(path_cutoff, gap, i)) {
sort_again = true;
memmove(path, path_cutoff, STRLEN(path_cutoff) + 1);
@@ -1005,7 +1005,7 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
// unique path. We start at the end of the path. */
pathsep_p = path + len - 1;
while (find_previous_pathsep(path, &pathsep_p)) {
- if (vim_regexec(&regmatch, pathsep_p + 1, (colnr_T)0)
+ if (vim_regexec(&regmatch, (char *)pathsep_p + 1, (colnr_T)0)
&& is_unique(pathsep_p + 1, gap, i)
&& path_cutoff != NULL && pathsep_p + 1 >= path_cutoff) {
sort_again = true;
@@ -1877,7 +1877,7 @@ char *fix_fname(const char *fname)
fname = xstrdup(fname);
# ifdef USE_FNAME_CASE
- path_fix_case((char_u *)fname); // set correct case for file name
+ path_fix_case(fname); // set correct case for file name
# endif
return (char *)fname;
@@ -1889,17 +1889,17 @@ char *fix_fname(const char *fname)
/// Only required for file systems where case is ignored and preserved.
// TODO(SplinterOfChaos): Could also be used when mounting case-insensitive
// file systems.
-void path_fix_case(char_u *name)
+void path_fix_case(char *name)
FUNC_ATTR_NONNULL_ALL
{
FileInfo file_info;
- if (!os_fileinfo_link((char *)name, &file_info)) {
+ if (!os_fileinfo_link(name, &file_info)) {
return;
}
// Open the directory where the file is located.
- char_u *slash = STRRCHR(name, '/');
- char_u *tail;
+ char *slash = (char *)STRRCHR(name, '/');
+ char *tail;
Directory dir;
bool ok;
if (slash == NULL) {
@@ -1907,7 +1907,7 @@ void path_fix_case(char_u *name)
tail = name;
} else {
*slash = NUL;
- ok = os_scandir(&dir, (char *)name);
+ ok = os_scandir(&dir, name);
*slash = '/';
tail = slash + 1;
}
@@ -1916,8 +1916,8 @@ void path_fix_case(char_u *name)
return;
}
- char_u *entry;
- while ((entry = (char_u *)os_scandir_next(&dir))) {
+ char *entry;
+ while ((entry = (char *)os_scandir_next(&dir))) {
// Only accept names that differ in case and are the same byte
// length. TODO: accept different length name.
if (STRICMP(tail, entry) == 0 && STRLEN(tail) == STRLEN(entry)) {
@@ -1956,9 +1956,9 @@ int after_pathsep(const char *b, const char *p)
*/
bool same_directory(char_u *f1, char_u *f2)
{
- char_u ffname[MAXPATHL];
- char_u *t1;
- char_u *t2;
+ char ffname[MAXPATHL];
+ char *t1;
+ char *t2;
// safety check
if (f1 == NULL || f2 == NULL) {
@@ -1967,8 +1967,8 @@ bool same_directory(char_u *f1, char_u *f2)
(void)vim_FullName((char *)f1, (char *)ffname, MAXPATHL, FALSE);
t1 = path_tail_with_sep(ffname);
- t2 = path_tail_with_sep(f2);
- return t1 - ffname == t2 - f2
+ t2 = path_tail_with_sep((char *)f2);
+ return t1 - ffname == (char_u *)t2 - f2
&& pathcmp((char *)ffname, (char *)f2, (int)(t1 - ffname)) == 0;
}
@@ -2246,7 +2246,7 @@ int match_suffix(char_u *fname)
size_t fnamelen = STRLEN(fname);
size_t setsuflen = 0;
for (char_u *setsuf = p_su; *setsuf;) {
- setsuflen = copy_option_part(&setsuf, suf_buf, MAXSUFLEN, ".,");
+ setsuflen = copy_option_part((char **)&setsuf, (char *)suf_buf, MAXSUFLEN, ".,");
if (setsuflen == 0) {
char_u *tail = (char_u *)path_tail((char *)fname);
diff --git a/src/nvim/po/af.po b/src/nvim/po/af.po
index a76dd8eeea..82345f8a46 100644
--- a/src/nvim/po/af.po
+++ b/src/nvim/po/af.po
@@ -34,6 +34,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO_8859-1\n"
"Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=2; plural=n!=1;\n"
#~ msgid "[Location List]"
#~ msgstr ""
@@ -645,11 +646,11 @@ msgstr "&Ok"
#~ msgid "filter() argument"
#~ msgstr ""
-#, fuzzy, c-format
-#~ msgid "+-%s%3ld line: "
-#~ msgid_plural "+-%s%3ld lines: "
-#~ msgstr[0] "+-%s%3ld reëls: "
-#~ msgstr[1] "+-%s%3ld reëls: "
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld reëls: "
+msgstr[1] "+-%s%3ld reëls: "
#, fuzzy, c-format
#~ msgid "E700: Unknown function: %s"
diff --git a/src/nvim/po/de.po b/src/nvim/po/de.po
index 740e9e5f6a..2dde77e9f7 100644
--- a/src/nvim/po/de.po
+++ b/src/nvim/po/de.po
@@ -18,6 +18,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO_8859-1\n"
"Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../api/private/helpers.c:197
msgid "Unable to get option value"
@@ -6233,8 +6234,10 @@ msgstr "filter()-Argument"
#: ../eval.c:8717
#, c-format
-msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld Zeilen: "
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld Zeile: "
+msgstr[1] "+-%s%3ld Zeilen: "
#: ../eval.c:8779
#, c-format
diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po
index 9b374e91ae..1c503d0a84 100644
--- a/src/nvim/po/eo.po
+++ b/src/nvim/po/eo.po
@@ -25,6 +25,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "E831: bf_key_init() called with empty password"
msgstr "E831: bf_key_init() alvokita kun malplena pasvorto"
@@ -1932,6 +1933,12 @@ msgstr "E350: Ne eblas krei faldon per la aktuala 'foldmethod'"
msgid "E351: Cannot delete fold with current 'foldmethod'"
msgstr "E351: Ne eblas forviÅi faldon per la aktuala 'foldmethod'"
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld linio: "
+msgstr[1] "+-%s%3ld linioj: "
+
msgid "E222: Add to read buffer"
msgstr "E222: Aldoni al lega bufro"
diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po
index 064484d1a4..adea651b7c 100644
--- a/src/nvim/po/es.po
+++ b/src/nvim/po/es.po
@@ -19,6 +19,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: octect-stream\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../api/private/helpers.c:201
#, fuzzy
@@ -786,8 +787,10 @@ msgstr "-c [argumentos]"
#: ../eval.c:9229
#, c-format
-msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld líneas: "
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld línea: "
+msgstr[1] "+-%s%3ld líneas: "
#: ../eval.c:9291
#, c-format
diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po
index 77d5f7f826..f10d4ce977 100644
--- a/src/nvim/po/fi.po
+++ b/src/nvim/po/fi.po
@@ -841,9 +841,11 @@ msgstr "map()-argumentti"
msgid "filter() argument"
msgstr "filter()-argumentti"
-#, fuzzy, c-format
-#~ msgid "+-%s%3ld lines: "
-#~ msgstr "+-%s%3ld rivi: "
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+--%s%3ld rivi: "
+msgstr[1] "+--%s%3ld riviä: "
#, c-format
msgid "E700: Unknown function: %s"
diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po
index 6df7741f1a..614ba013e6 100644
--- a/src/nvim/po/fr.po
+++ b/src/nvim/po/fr.po
@@ -1574,6 +1574,12 @@ msgid_plural "+--%3ld lines folded "
msgstr[0] "+--%3ld ligne déplacée "
msgstr[1] "+--%3ld lignes déplacées "
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld ligne : "
+msgstr[1] "+-%s%3ld lignes : "
+
msgid "E222: Add to read buffer"
msgstr "E222: Ajout au tampon de lecture"
diff --git a/src/nvim/po/ga.po b/src/nvim/po/ga.po
index ff16a87dbc..1c25ee481c 100644
--- a/src/nvim/po/ga.po
+++ b/src/nvim/po/ga.po
@@ -7103,13 +7103,14 @@ msgstr ""
"Níorbh fhéidir an chonair a shocrú: ní liosta é sys.path\n"
"Ba chóir duit vim.VIM_SPECIAL_PATH a cheangal le deireadh sys.path"
-#~ msgid "+-%s%3ld line: "
-#~ msgid_plural "+-%s%3ld lines: "
-#~ msgstr[0] "+-%s%3ld líne: "
-#~ msgstr[1] "+-%s%3ld líne: "
-#~ msgstr[2] "+-%s%3ld líne: "
-#~ msgstr[3] "+-%s%3ld líne: "
-#~ msgstr[4] "+-%s%3ld líne: "
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld líne: "
+msgstr[1] "+-%s%3ld líne: "
+msgstr[2] "+-%s%3ld líne: "
+msgstr[3] "+-%s%3ld líne: "
+msgstr[4] "+-%s%3ld líne: "
#~ msgid "+--%3ld line folded "
#~ msgid_plural "+--%3ld lines folded "
diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po
index dfabc4bee0..313280c807 100644
--- a/src/nvim/po/it.po
+++ b/src/nvim/po/it.po
@@ -25,6 +25,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO_8859-1\n"
"Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../api/private/helpers.c:201
msgid "Unable to get option value"
diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po
index 9633bec9f2..d7d0faca80 100644
--- a/src/nvim/po/ja.euc-jp.po
+++ b/src/nvim/po/ja.euc-jp.po
@@ -1837,6 +1837,11 @@ msgid "+--%3ld line folded "
msgid_plural "+--%3ld lines folded "
msgstr[0] "+--%3ld ¹Ô¤¬ÀÞ¾ö¤Þ¤ì¤Þ¤·¤¿"
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld ¹Ô: "
+
msgid "E222: Add to read buffer"
msgstr "E222: ÆÉ¹þ¥Ð¥Ã¥Õ¥¡¤ØÄɲÃ"
diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po
index c363c00fa6..b56345e066 100644
--- a/src/nvim/po/ja.po
+++ b/src/nvim/po/ja.po
@@ -1837,6 +1837,11 @@ msgid "+--%3ld line folded "
msgid_plural "+--%3ld lines folded "
msgstr[0] "+--%3ld è¡ŒãŒæŠ˜ç•³ã¾ã‚Œã¾ã—ãŸ"
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld 行: "
+
msgid "E222: Add to read buffer"
msgstr "E222: 読込ãƒãƒƒãƒ•ã‚¡ã¸è¿½åŠ "
diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po
index 5d3e51b7e2..7566036d3e 100644
--- a/src/nvim/po/ru.po
+++ b/src/nvim/po/ru.po
@@ -17,6 +17,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: ../api/private/helpers.c:201
#, fuzzy
@@ -767,8 +769,11 @@ msgstr "параметра filter()"
#: ../eval.c:9229
#, c-format
-msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld Ñтрок: "
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld Ñтрока: "
+msgstr[1] "+-%s%3ld Ñтроки: "
+msgstr[2] "+-%s%3ld Ñтрок: "
#: ../eval.c:9291
#, c-format
diff --git a/src/nvim/po/sr.po b/src/nvim/po/sr.po
index d34c1c3100..3c45e1bf80 100644
--- a/src/nvim/po/sr.po
+++ b/src/nvim/po/sr.po
@@ -931,7 +931,8 @@ msgstr "&Ок"
msgid "+-%s%3ld line: "
msgid_plural "+-%s%3ld lines: "
msgstr[0] "+-%s%3ld линија: "
-msgstr[1] "+-%s%3ld линија: "
+msgstr[1] "+-%s%3ld линијe: "
+msgstr[2] "+-%s%3ld линија: "
#, c-format
msgid "E700: Unknown function: %s"
diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po
index 7f0fe6a197..da87d50683 100644
--- a/src/nvim/po/uk.po
+++ b/src/nvim/po/uk.po
@@ -22,6 +22,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n"
+"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
msgid "--Deleted--"
msgstr "--Знищено--"
@@ -852,6 +854,12 @@ msgstr ""
"%.*s"
#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld Ñ€Ñдок: "
+msgstr[1] "+-%s%3ld Ñ€Ñдків: "
+
+#, c-format
msgid "E474: Expected string end: %.*s"
msgstr "E474: ОчікувавÑÑ ÐºÑ–Ð½ÐµÑ†ÑŒ Ñ€Ñдка: %.*s"
diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po
index 1e329443ce..9a8cd38f5e 100644
--- a/src/nvim/po/zh_CN.UTF-8.po
+++ b/src/nvim/po/zh_CN.UTF-8.po
@@ -23,6 +23,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
#: ../api/private/helpers.c:201
#, fuzzy
@@ -781,8 +782,9 @@ msgstr "-c 傿•°"
#: ../eval.c:9229
#, c-format
-msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld 行: "
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld 行: "
#: ../eval.c:9291
#, c-format
diff --git a/src/nvim/po/zh_TW.UTF-8.po b/src/nvim/po/zh_TW.UTF-8.po
index c97f31ddcf..e2fb2d39d4 100644
--- a/src/nvim/po/zh_TW.UTF-8.po
+++ b/src/nvim/po/zh_TW.UTF-8.po
@@ -827,8 +827,9 @@ msgstr ""
#: ../eval.c:9229
#, c-format
-msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld 行: "
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld 行: "
#: ../eval.c:9291
#, fuzzy, c-format
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index ecaeca005d..841277f8f3 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -18,6 +18,7 @@
#include "nvim/ex_cmds.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/menu.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/popupmnu.h"
@@ -512,9 +513,13 @@ void pum_redraw(void)
char_u *st;
char_u saved = *p;
- *p = NUL;
+ if (saved != NUL) {
+ *p = NUL;
+ }
st = (char_u *)transstr((const char *)s, true);
- *p = saved;
+ if (saved != NUL) {
+ *p = saved;
+ }
if (pum_rl) {
char *rt = (char *)reverse_text(st);
@@ -721,8 +726,7 @@ static int pum_set_selected(int n, int repeat)
if (!resized
&& (curbuf->b_nwindows == 1)
&& (curbuf->b_fname == NULL)
- && (curbuf->b_p_bt[0] == 'n')
- && (curbuf->b_p_bt[2] == 'f')
+ && bt_nofile(curbuf)
&& (curbuf->b_p_bh[0] == 'w')) {
// Already a "wipeout" buffer, make it empty.
while (!buf_is_empty(curbuf)) {
@@ -932,3 +936,181 @@ void pum_set_event_info(dict_T *dict)
(void)tv_dict_add_bool(dict, S_LEN("scrollbar"),
pum_scrollbar ? kBoolVarTrue : kBoolVarFalse);
}
+
+static void pum_position_at_mouse(int min_width)
+{
+ pum_anchor_grid = mouse_grid;
+ if (Rows - mouse_row > pum_size) {
+ // Enough space below the mouse row.
+ pum_above = false;
+ pum_row = mouse_row + 1;
+ if (pum_height > Rows - pum_row) {
+ pum_height = Rows - pum_row;
+ }
+ } else {
+ // Show above the mouse row, reduce height if it does not fit.
+ pum_above = true;
+ pum_row = mouse_row - pum_size;
+ if (pum_row < 0) {
+ pum_height += pum_row;
+ pum_row = 0;
+ }
+ }
+ if (Columns - mouse_col >= pum_base_width || Columns - mouse_col > min_width) {
+ // Enough space to show at mouse column.
+ pum_col = mouse_col;
+ } else {
+ // Not enough space, right align with window.
+ pum_col = Columns - (pum_base_width > min_width ? min_width : pum_base_width);
+ }
+
+ pum_width = Columns - pum_col;
+ if (pum_width > pum_base_width + 1) {
+ pum_width = pum_base_width + 1;
+ }
+}
+
+/// Select the pum entry at the mouse position.
+static void pum_select_mouse_pos(void)
+{
+ if (mouse_grid == pum_grid.handle) {
+ pum_selected = mouse_row;
+ return;
+ } else if (mouse_grid != pum_anchor_grid) {
+ pum_selected = -1;
+ return;
+ }
+
+ int idx = mouse_row - pum_row;
+
+ if (idx < 0 || idx >= pum_size) {
+ pum_selected = -1;
+ } else if (*pum_array[idx].pum_text != NUL) {
+ pum_selected = idx;
+ }
+}
+
+/// Execute the currently selected popup menu item.
+static void pum_execute_menu(vimmenu_T *menu, int mode)
+{
+ int idx = 0;
+ exarg_T ea;
+
+ for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
+ if ((mp->modes & mp->enabled & mode) && idx++ == pum_selected) {
+ memset(&ea, 0, sizeof(ea));
+ execute_menu(&ea, mp, -1);
+ break;
+ }
+ }
+}
+
+/// Open the terminal version of the popup menu and don't return until it is closed.
+void pum_show_popupmenu(vimmenu_T *menu)
+{
+ pum_undisplay(true);
+ pum_size = 0;
+ int mode = get_menu_mode_flag();
+
+ for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
+ if (menu_is_separator(mp->dname) || (mp->modes & mp->enabled & mode)) {
+ pum_size++;
+ }
+ }
+
+ // When there are only Terminal mode menus, using "popup Edit" results in
+ // pum_size being zero.
+ if (pum_size <= 0) {
+ emsg(e_menuothermode);
+ return;
+ }
+
+ int idx = 0;
+ pumitem_T *array = (pumitem_T *)xcalloc((size_t)pum_size, sizeof(pumitem_T));
+
+ for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
+ if (menu_is_separator(mp->dname)) {
+ array[idx++].pum_text = (char_u *)"";
+ } else if (mp->modes & mp->enabled & mode) {
+ array[idx++].pum_text = (char_u *)mp->dname;
+ }
+ }
+
+ pum_array = array;
+ pum_compute_size();
+ pum_scrollbar = 0;
+ pum_height = pum_size;
+ pum_position_at_mouse(20);
+
+ pum_selected = -1;
+ pum_first = 0;
+
+ for (;;) {
+ pum_is_visible = true;
+ pum_is_drawn = true;
+ pum_redraw();
+ setcursor_mayforce(true);
+ ui_flush();
+
+ int c = vgetc();
+ if (c == ESC || c == Ctrl_C) {
+ break;
+ } else if (c == CAR || c == NL) {
+ // enter: select current item, if any, and close
+ pum_execute_menu(menu, mode);
+ break;
+ } else if (c == 'k' || c == K_UP || c == K_MOUSEUP) {
+ // cursor up: select previous item
+ while (pum_selected > 0) {
+ pum_selected--;
+ if (*array[pum_selected].pum_text != NUL) {
+ break;
+ }
+ }
+ } else if (c == 'j' || c == K_DOWN || c == K_MOUSEDOWN) {
+ // cursor down: select next item
+ while (pum_selected < pum_size - 1) {
+ pum_selected++;
+ if (*array[pum_selected].pum_text != NUL) {
+ break;
+ }
+ }
+ } else if (c == K_RIGHTMOUSE) {
+ // Right mouse down: reposition the menu.
+ vungetc(c);
+ break;
+ } else if (c == K_LEFTDRAG || c == K_RIGHTDRAG || c == K_MOUSEMOVE) {
+ // mouse moved: select item in the mouse row
+ pum_select_mouse_pos();
+ } else if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM || c == K_RIGHTRELEASE) {
+ // left mouse click: select clicked item, if any, and close;
+ // right mouse release: select clicked item, close if any
+ pum_select_mouse_pos();
+ if (pum_selected >= 0) {
+ pum_execute_menu(menu, mode);
+ break;
+ }
+ if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM) {
+ break;
+ }
+ }
+ }
+
+ xfree(array);
+ pum_undisplay(true);
+}
+
+void pum_make_popup(const char *path_name, int use_mouse_pos)
+{
+ if (!use_mouse_pos) {
+ // Hack: set mouse position at the cursor so that the menu pops up
+ // around there.
+ mouse_row = curwin->w_winrow + curwin->w_wrow;
+ mouse_col = curwin->w_wincol + curwin->w_wcol;
+ }
+
+ vimmenu_T *menu = menu_find(path_name);
+ if (menu != NULL) {
+ pum_show_popupmenu(menu);
+ }
+}
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index edf964f5dd..2138437b29 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -1563,7 +1563,7 @@ static int qf_parse_get_fields(char *linebuf, size_t linelen, efm_T *fmt_ptr, qf
// Always ignore case when looking for a matching error.
regmatch.rm_ic = true;
regmatch.regprog = fmt_ptr->prog;
- r = vim_regexec(&regmatch, (char_u *)linebuf, (colnr_T)0);
+ r = vim_regexec(&regmatch, linebuf, (colnr_T)0);
fmt_ptr->prog = regmatch.regprog;
if (r) {
status = qf_parse_match(linebuf, linelen, fmt_ptr, &regmatch, fields,
@@ -2139,7 +2139,7 @@ static int qf_get_fnum(qf_list_T *qfl, char *directory, char *fname)
xfree(ptr);
} else {
xfree(qf_last_bufname);
- buf = buflist_new((char_u *)bufname, NULL, (linenr_T)0, BLN_NOOPT);
+ buf = buflist_new(bufname, NULL, (linenr_T)0, BLN_NOOPT);
qf_last_bufname = (bufname == ptr) ? bufname : xstrdup(bufname);
set_bufref(&qf_last_bufref, buf);
}
@@ -3301,7 +3301,7 @@ static void qf_msg(qf_info_T *qi, int which, char *lead)
}
STRLCAT(buf, title, IOSIZE);
}
- trunc_string((char_u *)buf, (char_u *)buf, Columns - 1, IOSIZE);
+ trunc_string(buf, buf, Columns - 1, IOSIZE);
msg(buf);
}
@@ -4035,7 +4035,7 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli
// buffer.
if (first_bufline
&& (errbuf->b_sfname == NULL
- || path_is_absolute(errbuf->b_sfname))) {
+ || path_is_absolute((char_u *)errbuf->b_sfname))) {
if (*dirname == NUL) {
os_dirname((char_u *)dirname, MAXPATHL);
}
@@ -5462,7 +5462,7 @@ static int vgr_process_files(win_T *wp, qf_info_T *qi, vgr_args_T *cmd_args, boo
vgr_display_fname(fname);
}
- buf_T *buf = buflist_findname_exp((char_u *)cmd_args->fnames[fi]);
+ buf_T *buf = buflist_findname_exp(cmd_args->fnames[fi]);
bool using_dummy;
if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
// Remember that a buffer with this name already exists.
@@ -6936,8 +6936,7 @@ void ex_cbuffer(exarg_T *eap)
qf_title = qf_cmdtitle(*eap->cmdlinep);
if (buf->b_sfname) {
- vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)",
- qf_title, (char *)buf->b_sfname);
+ vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)", qf_title, buf->b_sfname);
qf_title = (char *)IObuff;
}
@@ -7099,7 +7098,7 @@ static void hgr_search_file(qf_list_T *qfl, char *fname, regmatch_T *p_regmatch)
while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) {
char *line = (char *)IObuff;
- if (vim_regexec(p_regmatch, (char_u *)line, (colnr_T)0)) {
+ if (vim_regexec(p_regmatch, line, (colnr_T)0)) {
int l = (int)STRLEN(line);
// remove trailing CR, LF, spaces, etc.
@@ -7181,7 +7180,7 @@ static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char
// Go through all directories in 'runtimepath'
char *p = (char *)p_rtp;
while (*p != NUL && !got_int) {
- copy_option_part((char_u **)&p, NameBuff, MAXPATHL, ",");
+ copy_option_part(&p, (char *)NameBuff, MAXPATHL, ",");
hgr_search_files_in_dir(qfl, (char *)NameBuff, p_regmatch, (char *)lang);
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 45f2cf0e1d..4c49d30819 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -2327,7 +2327,6 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags)
{
regprog_T *prog = NULL;
char_u *expr = (char_u *)expr_arg;
- int save_called_emsg;
regexp_engine = p_re;
@@ -2360,8 +2359,7 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags)
//
// First try the NFA engine, unless backtracking was requested.
//
- save_called_emsg = called_emsg;
- called_emsg = false;
+ const int called_emsg_before = called_emsg;
if (regexp_engine != BACKTRACKING_ENGINE) {
prog = nfa_regengine.regcomp(expr,
re_flags + (regexp_engine == AUTOMATIC_ENGINE ? RE_AUTO : 0));
@@ -2388,13 +2386,12 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags)
// also fails for patterns that it can't handle well but are still valid
// patterns, thus a retry should work.
// But don't try if an error message was given.
- if (regexp_engine == AUTOMATIC_ENGINE && !called_emsg) {
+ if (regexp_engine == AUTOMATIC_ENGINE && called_emsg == called_emsg_before) {
regexp_engine = BACKTRACKING_ENGINE;
report_re_switch(expr);
prog = bt_regengine.regcomp(expr, re_flags);
}
}
- called_emsg |= save_called_emsg;
if (prog != NULL) {
// Store the info needed to call regcomp() again when the engine turns out
@@ -2516,9 +2513,9 @@ bool vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line, colnr_T
// Note: "rmp->regprog" may be freed and changed.
// Return true if there is a match, false if not.
-bool vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col)
+bool vim_regexec(regmatch_T *rmp, char *line, colnr_T col)
{
- return vim_regexec_string(rmp, line, col, false);
+ return vim_regexec_string(rmp, (char_u *)line, col, false);
}
// Like vim_regexec(), but consider a "\n" in "line" to be a line break.
diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c
index 03e4d74f14..272429bb91 100644
--- a/src/nvim/regexp_bt.c
+++ b/src/nvim/regexp_bt.c
@@ -3706,7 +3706,8 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
pos_T *pos;
size_t col = REG_MULTI ? rex.input - rex.line : 0;
- pos = getmark_buf(rex.reg_buf, mark, false);
+ // fm will be NULL if the mark is not set in reg_buf
+ fmark_T *fm = mark_get(rex.reg_buf, curwin, NULL, kMarkBufLocal, mark);
// Line may have been freed, get it again.
if (REG_MULTI) {
@@ -3714,10 +3715,11 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
rex.input = rex.line + col;
}
- if (pos == NULL // mark doesn't exist
- || pos->lnum <= 0) { // mark isn't set in reg_buf
+ if (fm == NULL // mark doesn't exist
+ || fm->mark.lnum <= 0) { // mark isn't set in reg_buf
status = RA_NOMATCH;
} else {
+ pos = &fm->mark;
const colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum
&& pos->col == MAXCOL
? (colnr_T)STRLEN(reg_getline(pos->lnum - rex.reg_firstlnum))
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 1e8204085c..870af3eafc 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -6930,10 +6930,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
case NFA_MARK:
case NFA_MARK_GT:
case NFA_MARK_LT: {
- pos_T *pos;
+ fmark_T *fm;
size_t col = REG_MULTI ? rex.input - rex.line : 0;
-
- pos = getmark_buf(rex.reg_buf, t->state->val, false);
+ // fm will be NULL if the mark is not set, doesn't belong to reg_buf
+ fm = mark_get(rex.reg_buf, curwin, NULL, kMarkBufLocal, t->state->val);
// Line may have been freed, get it again.
if (REG_MULTI) {
@@ -6943,7 +6943,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
// Compare the mark position to the match position, if the mark
// exists and mark is set in reg_buf.
- if (pos != NULL && pos->lnum > 0) {
+ if (fm != NULL && fm->mark.lnum > 0) {
+ pos_T *pos = &fm->mark;
const colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum
&& pos->col == MAXCOL
? (colnr_T)STRLEN(reg_getline(pos->lnum - rex.reg_firstlnum))
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 28d85b54bd..045cee2439 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -93,7 +93,7 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback,
char_u *rtp = rtp_copy;
while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) {
// Copy the path from 'runtimepath' to buf[].
- copy_option_part(&rtp, buf, MAXPATHL, ",");
+ copy_option_part((char **)&rtp, (char *)buf, MAXPATHL, ",");
size_t buflen = STRLEN(buf);
// Skip after or non-after directories.
@@ -118,8 +118,7 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback,
while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
// Append the pattern from "name" to buf[].
assert(MAXPATHL >= (tail - buf));
- copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)),
- "\t ");
+ copy_option_part((char **)&np, (char *)tail, (size_t)(MAXPATHL - (tail - buf)), "\t ");
if (p_verbose > 10) {
verbose_enter();
@@ -252,8 +251,7 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void
while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
// Append the pattern from "name" to buf[].
assert(MAXPATHL >= (tail - buf));
- copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)),
- "\t ");
+ copy_option_part((char **)&np, (char *)tail, (size_t)(MAXPATHL - (tail - buf)), "\t ");
if (p_verbose > 10) {
verbose_enter();
@@ -513,7 +511,7 @@ RuntimeSearchPath runtime_search_path_build(void)
static char_u buf[MAXPATHL];
for (char *entry = (char *)p_pp; *entry != NUL;) {
char *cur_entry = entry;
- copy_option_part((char_u **)&entry, buf, MAXPATHL, ",");
+ copy_option_part(&entry, (char *)buf, MAXPATHL, ",");
String the_entry = { .data = cur_entry, .size = STRLEN(buf) };
@@ -524,7 +522,7 @@ RuntimeSearchPath runtime_search_path_build(void)
char *rtp_entry;
for (rtp_entry = (char *)p_rtp; *rtp_entry != NUL;) {
char *cur_entry = rtp_entry;
- copy_option_part((char_u **)&rtp_entry, buf, MAXPATHL, ",");
+ copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ",");
size_t buflen = STRLEN(buf);
if (path_is_after(buf, buflen)) {
@@ -558,7 +556,7 @@ RuntimeSearchPath runtime_search_path_build(void)
// "after" dirs in rtp
for (; *rtp_entry != NUL;) {
- copy_option_part((char_u **)&rtp_entry, buf, MAXPATHL, ",");
+ copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ",");
expand_rtp_entry(&search_path, &rtp_used, (char *)buf, path_is_after(buf, STRLEN(buf)));
}
@@ -700,7 +698,7 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
for (const char *entry = (const char *)p_rtp; *entry != NUL;) {
const char *cur_entry = entry;
- copy_option_part((char_u **)&entry, buf, MAXPATHL, ",");
+ copy_option_part((char **)&entry, (char *)buf, MAXPATHL, ",");
if (insp == NULL) {
add_pathsep((char *)buf);
char *const rtp_ffname = fix_fname((char *)buf);
@@ -849,7 +847,7 @@ static void add_pack_plugin(bool opt, char_u *fname, void *cookie)
const char *p = (const char *)p_rtp;
while (*p != NUL) {
- copy_option_part((char_u **)&p, (char_u *)buf, MAXPATHL, ",");
+ copy_option_part((char **)&p, buf, MAXPATHL, ",");
if (path_fnamecmp(buf, (char *)fname) == 0) {
found = true;
break;
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index ea75900ded..de837720c1 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -298,6 +298,13 @@ void redraw_win_signcol(win_T *wp)
}
}
+/// Update all windows that are editing the current buffer.
+void update_curbuf(int type)
+{
+ redraw_curbuf_later(type);
+ update_screen(type);
+}
+
/// Redraw the parts of the screen that is marked for redraw.
///
/// Most code shouldn't call this directly, rather use redraw_later() and
@@ -1862,7 +1869,7 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b
schar_from_ascii(dest[0], *p);
s->prev_c = u8c;
} else {
- if (p_arshape && !p_tbidi && arabic_char(u8c)) {
+ if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
// Do Arabic shaping.
int pc, pc1, nc;
int pcc[MAX_MCO];
@@ -3150,7 +3157,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
} else if (mb_l == 0) { // at the NUL at end-of-line
mb_l = 1;
- } else if (p_arshape && !p_tbidi && arabic_char(mb_c)) {
+ } else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c)) {
// Do Arabic shaping.
int pc, pc1, nc;
int pcc[MAX_MCO];
@@ -4838,7 +4845,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in
clen = len;
i = first_match;
- while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2) < Columns) {
+ while (clen + status_match_len(xp, L_MATCH(i)) + 2 < Columns) {
if (i == match) {
selstart = buf + len;
selstart_col = clen;
@@ -5309,7 +5316,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
}
fillchar = wp->w_p_fcs_chars.wbr;
- attr = (wp == curwin) ? HL_ATTR(HLF_WBR) : HL_ATTR(HLF_WBRNC);
+ attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC);
maxwidth = wp->w_width_inner;
use_sandbox = was_set_insecurely(wp, "winbar", 0);
@@ -5486,7 +5493,7 @@ static void win_redr_border(win_T *wp)
int *attrs = wp->w_float_config.border_attr;
int *adj = wp->w_border_adj;
- int irow = wp->w_height_inner, icol = wp->w_width_inner;
+ int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner;
if (adj[0]) {
grid_puts_line_start(grid, 0);
@@ -5750,12 +5757,17 @@ static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
width * sizeof(sattr_T));
}
-/*
- * Set cursor to its position in the current window.
- */
+/// Set cursor to its position in the current window.
void setcursor(void)
{
- if (redrawing()) {
+ setcursor_mayforce(false);
+}
+
+/// Set cursor to its position in the current window.
+/// @param force when true, also when not redrawing.
+void setcursor_mayforce(bool force)
+{
+ if (force || redrawing()) {
validate_cursor();
ScreenGrid *grid = &curwin->w_grid;
@@ -6153,6 +6165,10 @@ void clearmode(void)
static void recording_mode(int attr)
{
+ if (p_ch <= 0 && !ui_has(kUIMessages)) {
+ return;
+ }
+
msg_puts_attr(_("recording"), attr);
if (!shortmess(SHM_RECORDING)) {
char s[4];
@@ -6396,7 +6412,7 @@ void get_trans_bufname(buf_T *buf)
} else {
home_replace(buf, buf->b_fname, (char *)NameBuff, MAXPATHL, true);
}
- trans_characters(NameBuff, MAXPATHL);
+ trans_characters((char *)NameBuff, MAXPATHL);
}
/*
@@ -6457,7 +6473,8 @@ int redrawing(void)
*/
int messaging(void)
{
- return !(p_lz && char_avail() && !KeyTyped);
+ return !(p_lz && char_avail() && !KeyTyped)
+ && (p_ch > 0 || ui_has(kUIMessages));
}
/// Show current status info in ruler and various other places
@@ -6515,14 +6532,12 @@ static void win_redr_ruler(win_T *wp, bool always)
}
}
- if (*p_ruf) {
- int save_called_emsg = called_emsg;
- called_emsg = false;
+ if (*p_ruf && p_ch > 0 && !ui_has(kUIMessages)) {
+ const int called_emsg_before = called_emsg;
win_redr_custom(wp, false, true);
- if (called_emsg) {
+ if (called_emsg > called_emsg_before) {
set_string_option_direct("rulerformat", -1, "", OPT_FREE, SID_ERROR);
}
- called_emsg |= save_called_emsg;
return;
}
@@ -6596,7 +6611,7 @@ static void win_redr_ruler(win_T *wp, bool always)
(wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? (int64_t)0L
: (int64_t)wp->w_cursor.lnum);
size_t len = STRLEN(buffer);
- col_print((char_u *)buffer + len, RULER_BUF_LEN - len,
+ col_print(buffer + len, RULER_BUF_LEN - len,
empty_line ? 0 : (int)wp->w_cursor.col + 1,
(int)virtcol + 1);
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 2a248a53e7..f3061b4dc4 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -514,7 +514,7 @@ void last_pat_prog(regmmatch_T *regmatch)
--emsg_off;
}
-/// lowest level search function.
+/// Lowest level search function.
/// Search for 'count'th occurrence of pattern "pat" in direction "dir".
/// Start at position "pos" and return the found position in "pos".
///
@@ -556,7 +556,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
long nmatched;
int submatch = 0;
bool first_match = true;
- int save_called_emsg = called_emsg;
+ const int called_emsg_before = called_emsg;
bool break_loop = false;
linenr_T stop_lnum = 0; // stop after this line number when != 0
proftime_T *tm = NULL; // timeout limit or NULL
@@ -579,7 +579,6 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
/*
* find the string
*/
- called_emsg = FALSE;
do { // loop for count
// When not accepting a match at the start position set "extra_col" to a
// non-zero value. Don't do that when starting at MAXCOL, since MAXCOL + 1
@@ -651,7 +650,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
break;
}
// Abort searching on an error (e.g., out of stack).
- if (called_emsg || (timed_out != NULL && *timed_out)) {
+ if (called_emsg > called_emsg_before || (timed_out != NULL && *timed_out)) {
break;
}
if (nmatched > 0) {
@@ -908,7 +907,8 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
// Stop the search if wrapscan isn't set, "stop_lnum" is
// specified, after an interrupt, after a match and after looping
// twice.
- if (!p_ws || stop_lnum != 0 || got_int || called_emsg
+ if (!p_ws || stop_lnum != 0 || got_int
+ || called_emsg > called_emsg_before
|| (timed_out != NULL && *timed_out)
|| break_loop
|| found || loop) {
@@ -933,7 +933,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
extra_arg->sa_wrapped = true;
}
}
- if (got_int || called_emsg
+ if (got_int || called_emsg > called_emsg_before
|| (timed_out != NULL && *timed_out)
|| break_loop) {
break;
@@ -942,8 +942,6 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
vim_regfree(regmatch.regprog);
- called_emsg |= save_called_emsg;
-
if (!found) { // did not find it
if (got_int) {
emsg(_(e_interr));
@@ -3991,8 +3989,7 @@ static int find_prev_quote(char_u *line, int col_start, int quotechar, char_u *e
}
if (n & 1) {
col_start -= n; // uneven number of escape chars, skip it
- } else if (line[col_start] ==
- quotechar) {
+ } else if (line[col_start] == quotechar) {
break;
}
}
@@ -4115,8 +4112,7 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
col_end = curwin->w_cursor.col;
}
}
- } else if (line[col_start] == quotechar
- || !vis_empty) {
+ } else if (line[col_start] == quotechar || !vis_empty) {
int first_col = col_start;
if (!vis_empty) {
@@ -4185,9 +4181,8 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
// Set start position. After vi" another i" must include the ".
// For v2i" include the quotes.
- if (!include && count < 2
- && (vis_empty || !inside_quotes)) {
- ++col_start;
+ if (!include && count < 2 && (vis_empty || !inside_quotes)) {
+ col_start++;
}
curwin->w_cursor.col = col_start;
if (VIsual_active) {
@@ -4412,7 +4407,7 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct
int nmatched = 0;
int result = -1;
pos_T pos;
- int save_called_emsg = called_emsg;
+ const int called_emsg_before = called_emsg;
int flag = 0;
if (pattern == NULL) {
@@ -4438,7 +4433,6 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct
SEARCH_KEEP + flag, RE_SEARCH, NULL) != FAIL) {
// Zero-width pattern should match somewhere, then we can check if
// start and end are in the same position.
- called_emsg = false;
do {
regmatch.startpos[0].col++;
nmatched = vim_regexec_multi(&regmatch, curwin, curbuf,
@@ -4452,14 +4446,13 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct
? regmatch.startpos[0].col < pos.col
: regmatch.startpos[0].col > pos.col);
- if (!called_emsg) {
+ if (called_emsg == called_emsg_before) {
result = (nmatched != 0
&& regmatch.startpos[0].lnum == regmatch.endpos[0].lnum
&& regmatch.startpos[0].col == regmatch.endpos[0].col);
}
}
- called_emsg |= save_called_emsg;
vim_regfree(regmatch.regprog);
return result;
}
@@ -5306,6 +5299,16 @@ void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
do_fuzzymatch(argvars, rettv, true);
}
+/// Get line "lnum" and copy it into "buf[LSIZE]".
+/// The copy is made because the regexp may make the line invalid when using a
+/// mark.
+static char_u *get_line_and_copy(linenr_T lnum, char_u *buf)
+{
+ char_u *line = ml_get(lnum);
+ STRLCPY(buf, line, LSIZE);
+ return buf;
+}
+
/// Find identifiers or defines in included files.
/// If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase.
///
@@ -5402,13 +5405,13 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
if (lnum > end_lnum) { // do at least one line
lnum = end_lnum;
}
- line = ml_get(lnum);
+ line = get_line_and_copy(lnum, file_line);
for (;;) {
if (incl_regmatch.regprog != NULL
- && vim_regexec(&incl_regmatch, line, (colnr_T)0)) {
+ && vim_regexec(&incl_regmatch, (char *)line, (colnr_T)0)) {
char_u *p_fname = (curr_fname == (char_u *)curbuf->b_fname)
- ? curbuf->b_ffname : curr_fname;
+ ? (char_u *)curbuf->b_ffname : curr_fname;
if (inc_opt != NULL && strstr((char *)inc_opt, "\\zs") != NULL) {
// Use text from '\zs' to '\ze' (or end) of 'include'.
@@ -5586,12 +5589,10 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
search_line:
define_matched = false;
if (def_regmatch.regprog != NULL
- && vim_regexec(&def_regmatch, line, (colnr_T)0)) {
- /*
- * Pattern must be first identifier after 'define', so skip
- * to that position before checking for match of pattern. Also
- * don't let it match beyond the end of this identifier.
- */
+ && vim_regexec(&def_regmatch, (char *)line, (colnr_T)0)) {
+ // Pattern must be first identifier after 'define', so skip
+ // to that position before checking for match of pattern. Also
+ // don't let it match beyond the end of this identifier.
p = def_regmatch.endp[0];
while (*p && !vim_iswordc(*p)) {
p++;
@@ -5618,7 +5619,7 @@ search_line:
matched = false;
}
} else if (regmatch.regprog != NULL
- && vim_regexec(&regmatch, line, (colnr_T)(p - line))) {
+ && vim_regexec(&regmatch, (char *)line, (colnr_T)(p - line))) {
matched = true;
startp = regmatch.startp[0];
// Check if the line is not a comment line (unless we are
@@ -5627,7 +5628,7 @@ search_line:
if (skip_comments) {
if ((*line != '#'
|| STRNCMP(skipwhite((char *)line + 1), "define", 6) != 0)
- && get_leader_len(line, NULL, false, true)) {
+ && get_leader_len((char *)line, NULL, false, true)) {
matched = false;
}
@@ -5692,7 +5693,7 @@ search_line:
if (lnum >= end_lnum) {
goto exit_matched;
}
- line = ml_get(++lnum);
+ line = get_line_and_copy(++lnum, file_line);
} else if (vim_fgets(line = file_line,
LSIZE, files[depth].fp)) {
goto exit_matched;
@@ -5884,7 +5885,7 @@ exit_matched:
if (++lnum > end_lnum) {
break;
}
- line = ml_get(lnum);
+ line = get_line_and_copy(lnum, file_line);
}
already = NULL;
}
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 32a0f3902d..6e80b550d8 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1328,11 +1328,11 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
char *const sfname =
(char *)path_try_shorten_fname((char_u *)cur_entry.data.buffer_list.buffers[i].fname);
buf_T *const buf =
- buflist_new((char_u *)cur_entry.data.buffer_list.buffers[i].fname, (char_u *)sfname, 0,
- BLN_LISTED);
+ buflist_new(cur_entry.data.buffer_list.buffers[i].fname, sfname, 0, BLN_LISTED);
if (buf != NULL) {
+ fmarkv_T view = INIT_FMARKV;
RESET_FMARK(&buf->b_last_cursor,
- cur_entry.data.buffer_list.buffers[i].pos, 0);
+ cur_entry.data.buffer_list.buffers[i].pos, 0, view);
buflist_setfpos(buf, curwin, buf->b_last_cursor.mark.lnum,
buf->b_last_cursor.mark.col, false);
buf->additional_data =
@@ -2306,7 +2306,7 @@ static inline ShadaEntry shada_get_buflist(khash_t(bufset) *const removable_bufs
}
buflist_entry.data.buffer_list.buffers[i] = (struct buffer_list_buffer) {
.pos = buf->b_last_cursor.mark,
- .fname = (char *)buf->b_ffname,
+ .fname = buf->b_ffname,
.additional_data = buf->additional_data,
};
i++;
@@ -2444,7 +2444,7 @@ static inline void replace_numbered_mark(WriteMergerState *const wms, const size
static inline void find_removable_bufs(khash_t(bufset) *removable_bufs)
{
FOR_ALL_BUFFERS(buf) {
- if (buf->b_ffname != NULL && shada_removable((char *)buf->b_ffname)) {
+ if (buf->b_ffname != NULL && shada_removable(buf->b_ffname)) {
int kh_ret;
(void)kh_put(bufset, removable_bufs, (uintptr_t)buf, &kh_ret);
}
@@ -2803,7 +2803,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
.mark = curwin->w_cursor,
.name = '0',
.additional_data = NULL,
- .fname = (char *)curbuf->b_ffname,
+ .fname = curbuf->b_ffname,
}
}
},
@@ -2997,7 +2997,7 @@ shada_write_file_open: {}
}
if (nomerge) {
shada_write_file_nomerge: {}
- char *const tail = (char *)path_tail_with_sep((char_u *)fname);
+ char *const tail = path_tail_with_sep(fname);
if (tail != fname) {
const char tail_save = *tail;
*tail = NUL;
@@ -3973,9 +3973,9 @@ static bool shada_removable(const char *name)
char part[MAXPATHL + 1];
bool retval = false;
- char *new_name = (char *)home_replace_save(NULL, (char_u *)name);
+ char *new_name = home_replace_save(NULL, (char *)name);
for (p = (char *)p_shada; *p;) {
- (void)copy_option_part((char_u **)&p, (char_u *)part, ARRAY_SIZE(part), ", ");
+ (void)copy_option_part(&p, part, ARRAY_SIZE(part), ", ");
if (part[0] == 'r') {
home_replace(NULL, part + 1, (char *)NameBuff, MAXPATHL, true);
size_t n = STRLEN(NameBuff);
@@ -4025,8 +4025,7 @@ static inline size_t shada_init_jumps(PossiblyFreedShadaEntry *jumps,
continue;
}
const char *const fname =
- (char *)(fm.fmark.fnum ==
- 0 ? (fm.fname == NULL ? NULL : (char_u *)fm.fname) : buf ? buf->b_ffname : NULL);
+ (fm.fmark.fnum == 0 ? (fm.fname == NULL ? NULL : fm.fname) : buf ? buf->b_ffname : NULL);
if (fname == NULL) {
continue;
}
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index 940cd1d274..9a4b304d6c 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -100,10 +100,9 @@ static signgroup_T *sign_group_ref(const char_u *groupname)
/// removed, then remove the group.
static void sign_group_unref(char_u *groupname)
{
- hashitem_T *hi;
signgroup_T *group;
- hi = hash_find(&sg_table, groupname);
+ hashitem_T *hi = hash_find(&sg_table, (char *)groupname);
if (!HASHITEM_EMPTY(hi)) {
group = HI2SG(hi);
group->sg_refcount--;
@@ -136,7 +135,7 @@ static int sign_group_get_next_signid(buf_T *buf, const char_u *groupname)
int found = false;
if (groupname != NULL) {
- hi = hash_find(&sg_table, groupname);
+ hi = hash_find(&sg_table, (char *)groupname);
if (HASHITEM_EMPTY(hi)) {
return id;
}
@@ -647,7 +646,7 @@ static int buf_findsign_id(buf_T *buf, linenr_T lnum, char_u *groupname)
}
/// Delete signs in buffer "buf".
-void buf_delete_signs(buf_T *buf, char_u *group)
+void buf_delete_signs(buf_T *buf, char *group)
{
sign_entry_T *sign;
sign_entry_T **lastp; // pointer to pointer to current sign
@@ -662,7 +661,7 @@ void buf_delete_signs(buf_T *buf, char_u *group)
lastp = &buf->b_signlist;
for (sign = buf->b_signlist; sign != NULL; sign = next) {
next = sign->se_next;
- if (sign_in_group(sign, group)) {
+ if (sign_in_group(sign, (char_u *)group)) {
*lastp = next;
if (next != NULL) {
next->se_prev = sign->se_prev;
@@ -1086,7 +1085,7 @@ static int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T at
if (sign_id == 0) {
// Delete all the signs in the specified buffer
redraw_buf_later(buf, NOT_VALID);
- buf_delete_signs(buf, sign_group);
+ buf_delete_signs(buf, (char *)sign_group);
} else {
linenr_T lnum;
@@ -1173,7 +1172,7 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline)
if (*arg == NUL) {
break;
}
- p = skiptowhite_esc(arg);
+ p = (char_u *)skiptowhite_esc((char *)arg);
if (STRNCMP(arg, "icon=", 5) == 0) {
arg += 5;
XFREE_CLEAR(icon);
@@ -1273,7 +1272,7 @@ static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, char_u *sign_name, int i
// :sign unplace * group=*
FOR_ALL_BUFFERS(cbuf) {
if (cbuf->b_signlist != NULL) {
- buf_delete_signs(cbuf, group);
+ buf_delete_signs(cbuf, (char *)group);
}
}
}
@@ -1341,7 +1340,7 @@ static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *si
// first arg could be placed sign id
arg1 = arg;
if (ascii_isdigit(*arg)) {
- *signid = getdigits_int(&arg, true, 0);
+ *signid = getdigits_int((char **)&arg, true, 0);
if (!ascii_iswhite(*arg) && *arg != NUL) {
*signid = -1;
arg = arg1;
@@ -1388,12 +1387,12 @@ static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *si
} else if (STRNCMP(arg, "file=", 5) == 0) {
arg += 5;
filename = arg;
- *buf = buflist_findname_exp(arg);
+ *buf = buflist_findname_exp((char *)arg);
break;
} else if (STRNCMP(arg, "buffer=", 7) == 0) {
arg += 7;
filename = arg;
- *buf = buflist_findnr(getdigits_int(&arg, true, 0));
+ *buf = buflist_findnr(getdigits_int((char **)&arg, true, 0));
if (*skipwhite((char *)arg) != NUL) {
emsg(_(e_trailing));
}
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index e4805f3c4a..2aadc2258e 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -504,7 +504,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
// Check for end of sentence.
regmatch.regprog = wp->w_s->b_cap_prog;
regmatch.rm_ic = false;
- int r = vim_regexec(&regmatch, ptr, 0);
+ int r = vim_regexec(&regmatch, (char *)ptr, 0);
wp->w_s->b_cap_prog = regmatch.regprog;
if (r) {
*capcol = (int)(regmatch.endp[0] - ptr);
@@ -1469,7 +1469,9 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
}
// Copy the line into "buf" and append the start of the next line if
- // possible.
+ // possible. Note: this ml_get_buf() may make "line" invalid, check
+ // for empty line first.
+ bool empty_line = *skipwhite((const char *)line) == NUL;
STRCPY(buf, line);
if (lnum < wp->w_buffer->b_ml.ml_line_count) {
spell_cat_line(buf + STRLEN(buf),
@@ -1613,7 +1615,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
--capcol;
// But after empty line check first word in next line
- if (*skipwhite((char *)line) == NUL) {
+ if (empty_line) {
capcol = 0;
}
}
@@ -1908,12 +1910,11 @@ void count_common_word(slang_T *lp, char_u *word, int len, int count)
/// @param split word was split, less bonus
static int score_wordcount_adj(slang_T *slang, int score, char_u *word, bool split)
{
- hashitem_T *hi;
wordcount_T *wc;
int bonus;
int newscore;
- hi = hash_find(&slang->sl_wordcount, word);
+ hashitem_T *hi = hash_find(&slang->sl_wordcount, (char *)word);
if (!HASHITEM_EMPTY(hi)) {
wc = HI2WC(hi);
if (wc->wc_count < SCORE_THRES2) {
@@ -2083,7 +2084,7 @@ char *did_set_spelllang(win_T *wp)
// Loop over comma separated language names.
for (splp = spl_copy; *splp != NUL;) {
// Get one language name.
- copy_option_part(&splp, lang, MAXWLEN, ",");
+ copy_option_part((char **)&splp, (char *)lang, MAXWLEN, ",");
region = NULL;
len = (int)STRLEN(lang);
@@ -2215,7 +2216,7 @@ char *did_set_spelllang(win_T *wp)
int_wordlist_spl(spf_name);
} else {
// One entry in 'spellfile'.
- copy_option_part(&spf, spf_name, MAXPATHL - 5, ",");
+ copy_option_part((char **)&spf, (char *)spf_name, MAXPATHL - 5, ",");
STRCAT(spf_name, ".spl");
// If it was already found above then skip it.
@@ -2328,11 +2329,11 @@ char *did_set_spelllang(win_T *wp)
}
}
}
+ redraw_later(wp, NOT_VALID);
theend:
xfree(spl_copy);
recursive = false;
- redraw_later(wp, NOT_VALID);
return ret_msg;
}
@@ -2805,12 +2806,12 @@ int spell_check_sps(void)
sps_limit = 9999;
for (p = p_sps; *p != NUL;) {
- copy_option_part(&p, buf, MAXPATHL, ",");
+ copy_option_part((char **)&p, (char *)buf, MAXPATHL, ",");
f = 0;
if (ascii_isdigit(*buf)) {
s = buf;
- sps_limit = getdigits_int(&s, true, 0);
+ sps_limit = getdigits_int((char **)&s, true, 0);
if (*s != NUL && !ascii_isdigit(*s)) {
f = -1;
}
@@ -3121,7 +3122,7 @@ static bool check_need_cap(linenr_T lnum, colnr_T col)
if (p == line || spell_iswordp_nmw(p, curwin)) {
break;
}
- if (vim_regexec(&regmatch, p, 0)
+ if (vim_regexec(&regmatch, (char *)p, 0)
&& regmatch.endp[0] == line + endcol) {
need_cap = true;
break;
@@ -3328,7 +3329,7 @@ static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int ma
// Loop over the items in 'spellsuggest'.
for (p = sps_copy; *p != NUL;) {
- copy_option_part(&p, buf, MAXPATHL, ",");
+ copy_option_part((char **)&p, (char *)buf, MAXPATHL, ",");
if (STRNCMP(buf, "expr:", 5) == 0) {
// Evaluate an expression. Skip this when called recursively,
@@ -4034,8 +4035,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
break;
}
if ((sp->ts_complen == sp->ts_compsplit
- && WAS_BANNED(su, preword + sp->ts_prewordlen))
- || WAS_BANNED(su, preword)) {
+ && WAS_BANNED(su, (char *)preword + sp->ts_prewordlen))
+ || WAS_BANNED(su, (char *)preword)) {
if (slang->sl_compprog == NULL) {
break;
}
@@ -5661,7 +5662,7 @@ static bool similar_chars(slang_T *slang, int c1, int c2)
if (c1 >= 256) {
buf[utf_char2bytes(c1, (char *)buf)] = 0;
- hi = hash_find(&slang->sl_map_hash, (char_u *)buf);
+ hi = hash_find(&slang->sl_map_hash, buf);
if (HASHITEM_EMPTY(hi)) {
m1 = 0;
} else {
@@ -5676,7 +5677,7 @@ static bool similar_chars(slang_T *slang, int c1, int c2)
if (c2 >= 256) {
buf[utf_char2bytes(c2, (char *)buf)] = 0;
- hi = hash_find(&slang->sl_map_hash, (char_u *)buf);
+ hi = hash_find(&slang->sl_map_hash, buf);
if (HASHITEM_EMPTY(hi)) {
m2 = 0;
} else {
@@ -7022,8 +7023,9 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
n = arridx[depth] + curi[depth];
++curi[depth];
c = byts[n];
- if (c == 0) {
- // End of word, deal with the word.
+ if (c == 0 || depth >= MAXWLEN - 1) {
+ // End of word or reached maximum length, deal with the
+ // word.
// Don't use keep-case words in the fold-case tree,
// they will appear in the keep-case tree.
// Only use the word when the region matches.
@@ -7144,7 +7146,7 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
hashitem_T *hi;
// Include the word count for ":spelldump!".
- hi = hash_find(&slang->sl_wordcount, tw);
+ hi = hash_find(&slang->sl_wordcount, (char *)tw);
if (!HASHITEM_EMPTY(hi)) {
vim_snprintf((char *)IObuff, IOSIZE, "%s\t%d",
tw, HI2WC(hi)->wc_count);
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 07f3d39886..9d2fd2637d 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -733,7 +733,7 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
if (lp->sl_syllable == NULL) {
goto endFAIL;
}
- if (init_syl_tab(lp) == FAIL) {
+ if (init_syl_tab(lp) != OK) {
goto endFAIL;
}
break;
@@ -2351,7 +2351,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// "S" flag on all but the last block, thus we check for that
// and store it in ah_follows.
STRLCPY(key, items[1], AH_KEY_LEN);
- hi = hash_find(tp, key);
+ hi = hash_find(tp, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
cur_aff = HI2AH(hi);
if (cur_aff->ah_combine != (*items[2] == 'Y')) {
@@ -2379,7 +2379,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
|| cur_aff->ah_flag == aff->af_needcomp
|| cur_aff->ah_flag == aff->af_comproot) {
smsg(_("Affix also used for "
- "BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST"
+ "BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST "
"in %s line %d: %s"),
fname, lnum, items[1]);
}
@@ -2688,9 +2688,8 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
} else if (STRCMP(items[0], "COMMON") == 0) {
int i;
- for (i = 1; i < itemcnt; ++i) {
- if (HASHITEM_EMPTY(hash_find(&spin->si_commonwords,
- items[i]))) {
+ for (i = 1; i < itemcnt; i++) {
+ if (HASHITEM_EMPTY(hash_find(&spin->si_commonwords, (char *)items[i]))) {
p = vim_strsave(items[i]);
hash_add(&spin->si_commonwords, p);
}
@@ -2872,7 +2871,7 @@ static unsigned get_affitem(int flagtype, char_u **pp)
++*pp; // always advance, avoid getting stuck
return 0;
}
- res = getdigits_int(pp, true, 0);
+ res = getdigits_int((char **)pp, true, 0);
if (res == 0) {
res = ZERO_FLAG;
}
@@ -2932,7 +2931,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla
// Find the flag in the hashtable. If it was used before, use
// the existing ID. Otherwise add a new entry.
STRLCPY(key, prevp, p - prevp + 1);
- hi = hash_find(&aff->af_comp, key);
+ hi = hash_find(&aff->af_comp, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
id = HI2CI(hi)->ci_newID;
} else {
@@ -2997,7 +2996,7 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag)
case AFT_NUM:
for (p = afflist; *p != NUL;) {
- int digits = getdigits_int(&p, true, 0);
+ int digits = getdigits_int((char **)&p, true, 0);
assert(digits >= 0);
n = (unsigned int)digits;
if (n == 0) {
@@ -3359,7 +3358,7 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist
// A flag is a postponed prefix flag if it appears in "af_pref"
// and its ID is not zero.
STRLCPY(key, prevp, p - prevp + 1);
- hi = hash_find(&affile->af_pref, key);
+ hi = hash_find(&affile->af_pref, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
id = HI2AH(hi)->ah_newID;
if (id != 0) {
@@ -3392,7 +3391,7 @@ static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_affl
if (get_affitem(affile->af_flagtype, &p) != 0) {
// A flag is a compound flag if it appears in "af_comp".
STRLCPY(key, prevp, p - prevp + 1);
- hi = hash_find(&affile->af_comp, key);
+ hi = hash_find(&affile->af_comp, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
store_afflist[cnt++] = HI2CI(hi)->ci_newID;
}
@@ -3904,6 +3903,21 @@ static wordnode_T *wordtree_alloc(spellinfo_T *spin)
return (wordnode_T *)getroom(spin, sizeof(wordnode_T), true);
}
+/// Return true if "word" contains valid word characters.
+/// Control characters and trailing '/' are invalid. Space is OK.
+static bool valid_spell_word(const char_u *word, const char_u *end)
+{
+ if (!utf_valid_string(word, end)) {
+ return false;
+ }
+ for (const char_u *p = word; *p != NUL && p < end; p += utfc_ptr2len((const char *)p)) {
+ if (*p < ' ' || (p[0] == '/' && p[1] == NUL)) {
+ return false;
+ }
+ }
+ return true;
+}
+
/// Store a word in the tree(s).
/// Always store it in the case-folded tree. For a keep-case word this is
/// useful when the word can also be used with all caps (no WF_FIXCAP flag) and
@@ -3925,7 +3939,7 @@ static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, co
int res = OK;
// Avoid adding illegal bytes to the word tree.
- if (!utf_valid_string(word, NULL)) {
+ if (!valid_spell_word(word, word + len)) {
return FAIL;
}
@@ -5522,7 +5536,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
int i;
char_u *spf;
- if (!utf_valid_string(word, NULL)) {
+ if (!valid_spell_word(word, word + len)) {
emsg(_(e_illegal_character_in_word));
return;
}
@@ -5548,8 +5562,8 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
}
fnamebuf = xmalloc(MAXPATHL);
- for (spf = curwin->w_s->b_p_spf, i = 1; *spf != NUL; ++i) {
- copy_option_part(&spf, fnamebuf, MAXPATHL, ",");
+ for (spf = curwin->w_s->b_p_spf, i = 1; *spf != NUL; i++) {
+ copy_option_part((char **)&spf, (char *)fnamebuf, MAXPATHL, ",");
if (i == idx) {
break;
}
@@ -5561,7 +5575,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
}
// Check that the user isn't editing the .add file somewhere.
- buf = buflist_findname_exp(fnamebuf);
+ buf = buflist_findname_exp((char *)fnamebuf);
if (buf != NULL && buf->b_ml.ml_mfp == NULL) {
buf = NULL;
}
@@ -5623,7 +5637,8 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
// file. We may need to create the "spell" directory first. We
// already checked the runtime directory is writable in
// init_spellfile().
- if (!dir_of_file_exists(fname) && (p = path_tail_with_sep(fname)) != fname) {
+ if (!dir_of_file_exists(fname)
+ && (p = (char_u *)path_tail_with_sep((char *)fname)) != fname) {
int c = *p;
// The directory doesn't exist. Try creating it and opening
@@ -5701,7 +5716,7 @@ static void init_spellfile(void)
lstart - curbuf->b_s.b_p_spl);
} else {
// Copy the path from 'runtimepath' to buf[].
- copy_option_part(&rtp, buf, MAXPATHL, ",");
+ copy_option_part((char **)&rtp, (char *)buf, MAXPATHL, ",");
}
if (os_file_is_writable((char *)buf) == 2) {
// Use the first language name from 'spelllang' and the
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 9615423765..43dbeccf01 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -3005,7 +3005,7 @@ static int check_keyword_id(char_u *const line, const int startcol, int *const e
/// Accept a keyword at other levels only if it is in the contains list.
static keyentry_T *match_keyword(char_u *keyword, hashtab_T *ht, stateitem_T *cur_si)
{
- hashitem_T *hi = hash_find(ht, keyword);
+ hashitem_T *hi = hash_find(ht, (char *)keyword);
if (!HASHITEM_EMPTY(hi)) {
for (keyentry_T *kp = HI2KE(hi); kp != NULL; kp = kp->ke_next) {
if (current_next_list != 0
@@ -5123,7 +5123,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
ci->sp_off_flags |= (1 << idx);
if (idx == SPO_LC_OFF) { // lc=99
end += 3;
- *p = getdigits_int(&end, true, 0);
+ *p = getdigits_int((char **)&end, true, 0);
// "lc=" offset automatically sets "ms=" offset
if (!(ci->sp_off_flags & (1 << SPO_MS_OFF))) {
@@ -5134,10 +5134,10 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
end += 4;
if (*end == '+') {
end++;
- *p = getdigits_int(&end, true, 0); // positive offset
+ *p = getdigits_int((char **)&end, true, 0); // positive offset
} else if (*end == '-') {
end++;
- *p = -getdigits_int(&end, true, 0); // negative offset
+ *p = -getdigits_int((char **)&end, true, 0); // negative offset
}
}
if (*end != ',') {
@@ -5382,7 +5382,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
regmatch.rm_ic = TRUE;
id = 0;
for (int i = highlight_num_groups(); --i >= 0;) {
- if (vim_regexec(&regmatch, highlight_group_name(i), (colnr_T)0)) {
+ if (vim_regexec(&regmatch, (char *)highlight_group_name(i), (colnr_T)0)) {
if (round == 2) {
// Got more items than expected; can happen
// when adding items that match:
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 796a2fa5f3..28b3b6c1ef 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -116,7 +116,7 @@ static char_u *tagmatchname = NULL; // name of last used tag
* Tag for preview window is remembered separately, to avoid messing up the
* normal tagstack.
*/
-static taggy_T ptag_entry = { NULL, { { 0, 0, 0 }, 0, 0, NULL }, 0, 0, NULL };
+static taggy_T ptag_entry = { NULL, INIT_FMARK, 0, 0, NULL };
static int tfu_in_use = false; // disallow recursive call of tagfunc
@@ -167,7 +167,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
char_u **new_matches;
int use_tagstack;
int skip_msg = false;
- char_u *buf_ffname = curbuf->b_ffname; // name for priority computation
+ char_u *buf_ffname = (char_u *)curbuf->b_ffname; // name for priority computation
int use_tfu = 1;
// remember the matches for the last used tag
@@ -423,7 +423,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
buf_T *buf = buflist_findnr(cur_fnum);
if (buf != NULL) {
- buf_ffname = buf->b_ffname;
+ buf_ffname = (char_u *)buf->b_ffname;
}
}
@@ -1863,6 +1863,7 @@ parse_line:
// For "normal" tags: Do a quick check if the tag matches.
// This speeds up tag searching a lot!
if (orgpat.headlen) {
+ memset(&tagp, 0, sizeof(tagp));
tagp.tagname = lbuf;
tagp.tagname_end = (char_u *)vim_strchr((char *)lbuf, TAB);
if (tagp.tagname_end == NULL) {
@@ -2031,14 +2032,13 @@ parse_line:
cc = *tagp.tagname_end;
*tagp.tagname_end = NUL;
- match = vim_regexec(&orgpat.regmatch, tagp.tagname, (colnr_T)0);
+ match = vim_regexec(&orgpat.regmatch, (char *)tagp.tagname, (colnr_T)0);
if (match) {
matchoff = (int)(orgpat.regmatch.startp[0] - tagp.tagname);
if (orgpat.regmatch.rm_ic) {
- orgpat.regmatch.rm_ic = FALSE;
- match_no_ic = vim_regexec(&orgpat.regmatch, tagp.tagname,
- (colnr_T)0);
- orgpat.regmatch.rm_ic = TRUE;
+ orgpat.regmatch.rm_ic = false;
+ match_no_ic = vim_regexec(&orgpat.regmatch, (char *)tagp.tagname, (colnr_T)0);
+ orgpat.regmatch.rm_ic = true;
}
}
*tagp.tagname_end = (char_u)cc;
@@ -2423,7 +2423,7 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
* Copy next file name into buf.
*/
buf[0] = NUL;
- (void)copy_option_part(&tnp->tn_np, buf, MAXPATHL - 1, " ,");
+ (void)copy_option_part((char **)&tnp->tn_np, (char *)buf, MAXPATHL - 1, " ,");
r_ptr = vim_findfile_stopdir(buf);
// move the filename one char forward and truncate the
@@ -2436,7 +2436,7 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
r_ptr, 100,
FALSE, // don't free visited list
FINDFILE_FILE, // we search for a file
- tnp->tn_search_ctx, TRUE, curbuf->b_ffname);
+ tnp->tn_search_ctx, true, (char_u *)curbuf->b_ffname);
if (tnp->tn_search_ctx != NULL) {
tnp->tn_did_filefind_init = TRUE;
}
@@ -2749,7 +2749,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
// If it was a CTRL-W CTRL-] command split window now. For ":tab tag"
// open a new tab page.
if (postponed_split && (swb_flags & (SWB_USEOPEN | SWB_USETAB))) {
- buf_T *const existing_buf = buflist_findname_exp(fname);
+ buf_T *const existing_buf = buflist_findname_exp((char *)fname);
if (existing_buf != NULL) {
const win_T *wp = NULL;
@@ -2785,7 +2785,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
// A :ta from a help file will keep the b_help flag set. For ":ptag"
// we need to use the flag from the window where we came from.
if (l_g_do_tagpreview != 0) {
- keep_help_flag = curwin_save->w_buffer->b_help;
+ keep_help_flag = bt_help(curwin_save->w_buffer);
} else {
keep_help_flag = curbuf->b_help;
}
@@ -3112,11 +3112,11 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file)
if (pat[0] == '/') {
ret = find_tags(pat + 1, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC,
- TAG_MANY, curbuf->b_ffname);
+ TAG_MANY, (char_u *)curbuf->b_ffname);
} else {
ret = find_tags(pat, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC | TAG_NOIC,
- TAG_MANY, curbuf->b_ffname);
+ TAG_MANY, (char_u *)curbuf->b_ffname);
}
if (ret == OK && !tagnames) {
// Reorganize the tags for display and matching as strings of:
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 85517a71a4..be49048aec 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -228,7 +228,7 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
set_option_value("wrap", false, NULL, OPT_LOCAL);
set_option_value("list", false, NULL, OPT_LOCAL);
if (buf->b_ffname != NULL) {
- buf_set_term_title(buf, (char *)buf->b_ffname);
+ buf_set_term_title(buf, buf->b_ffname);
}
RESET_BINDING(curwin);
// Reset cursor in current window.
diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 8f97d959ce..4107df99d6 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -55,6 +55,14 @@ func CheckMSWindows()
endif
endfunc
+" Command to check for NOT running on MS-Windows
+command CheckNotMSWindows call CheckNotMSWindows()
+func CheckNotMSWindows()
+ if has('win32')
+ throw 'Skipped: does not work on MS-Windows'
+ endif
+endfunc
+
" Command to check for running on Unix
command CheckUnix call CheckUnix()
func CheckUnix()
@@ -129,14 +137,6 @@ func CheckEnglish()
endif
endfunc
-" Command to check for NOT running on MS-Windows
-command CheckNotMSWindows call CheckNotMSWindows()
-func CheckNotMSWindows()
- if has('win32')
- throw 'Skipped: does not work on MS-Windows'
- endif
-endfunc
-
" Command to check for not running under ASAN
command CheckNotAsan call CheckNotAsan()
func CheckNotAsan()
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index b0d872e392..6b16e888a9 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -153,6 +153,9 @@ func RunTheTest(test)
" directory after executing the test.
let save_cwd = getcwd()
+ " Align Nvim defaults to Vim.
+ source setup.vim
+
if exists("*SetUp")
try
call SetUp()
@@ -361,24 +364,25 @@ let s:flaky_tests = [
\ 'Test_cursorhold_insert()',
\ 'Test_exit_callback_interval()',
\ 'Test_map_timeout_with_timer_interrupt()',
- \ 'Test_oneshot()',
\ 'Test_out_cb()',
- \ 'Test_paused()',
\ 'Test_popup_and_window_resize()',
\ 'Test_quoteplus()',
\ 'Test_quotestar()',
\ 'Test_reltime()',
- \ 'Test_repeat_many()',
- \ 'Test_repeat_three()',
\ 'Test_state()',
- \ 'Test_stop_all_in_callback()',
\ 'Test_term_mouse_double_click_to_create_tab()',
\ 'Test_term_mouse_multiple_clicks_to_visually_select()',
\ 'Test_terminal_composing_unicode()',
\ 'Test_terminal_redir_file()',
\ 'Test_terminal_tmap()',
+ \ 'Test_timer_oneshot()',
+ \ 'Test_timer_paused()',
+ \ 'Test_timer_repeat_many()',
+ \ 'Test_timer_repeat_three()',
+ \ 'Test_timer_stop_all_in_callback()',
+ \ 'Test_timer_stop_in_callback()',
+ \ 'Test_timer_with_partial_callback()',
\ 'Test_termwinscroll()',
- \ 'Test_with_partial_callback()',
\ ]
" Locate Test_ functions and execute them.
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index f1092af358..e6c0762729 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -1,3 +1,35 @@
+if exists('s:did_load')
+ " Align Nvim defaults to Vim.
+ set backspace=
+ set complete=.,w,b,u,t,i
+ set directory&
+ set directory^=.
+ set fillchars=vert:\|,fold:-
+ set formatoptions=tcq
+ set fsync
+ set laststatus=1
+ set listchars=eol:$
+ set joinspaces
+ set nohidden nosmarttab noautoindent noautoread noruler noshowcmd
+ set nohlsearch noincsearch
+ set nrformats=bin,octal,hex
+ set shortmess=filnxtToOS
+ set sidescroll=0
+ set tags=./tags,tags
+ set undodir&
+ set undodir^=.
+ set wildoptions=
+ set startofline
+ set sessionoptions&
+ set sessionoptions+=options
+ set viewoptions&
+ set viewoptions+=options
+ set switchbuf=
+ " Make "Q" switch to Ex mode.
+ " This does not work for all tests.
+ nnoremap Q gQ
+endif
+
" Common preparations for running tests.
" Only load this once.
@@ -6,26 +38,6 @@ if exists('s:did_load')
endif
let s:did_load = 1
-" Align Nvim defaults to Vim.
-set backspace=
-set directory^=.
-set fillchars=vert:\|,fold:-
-set fsync
-set laststatus=1
-set listchars=eol:$
-set joinspaces
-set nohidden nosmarttab noautoindent noautoread complete-=i noruler noshowcmd
-set nrformats+=octal
-set shortmess-=F
-set sidescroll=0
-set tags=./tags,tags
-set undodir^=.
-set wildoptions=
-set startofline
-set sessionoptions+=options
-set viewoptions+=options
-set switchbuf=
-
" Clear Nvim default mappings.
mapclear
mapclear!
diff --git a/src/nvim/testdir/test_arabic.vim b/src/nvim/testdir/test_arabic.vim
index 450c6f98f5..272937387d 100644
--- a/src/nvim/testdir/test_arabic.vim
+++ b/src/nvim/testdir/test_arabic.vim
@@ -2,9 +2,8 @@
" NOTE: This just checks if the code works. If you know Arabic please add
" functional tests that check the shaping works with real text.
-if !has('arabic')
- throw 'Skipped: arabic feature missing'
-endif
+source check.vim
+CheckFeature arabic
source view_util.vim
@@ -563,3 +562,26 @@ func Test_shape_combination_isolated()
set arabicshape&
bwipe!
endfunc
+
+" Test for entering arabic character in a search command
+func Test_arabic_chars_in_search_cmd()
+ new
+ set arabic
+ call feedkeys("i\nsghl!\<C-^>vim\<C-^>", 'tx')
+ call cursor(1, 1)
+ call feedkeys("/^sghl!\<C-^>vim$\<C-^>\<CR>", 'tx')
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ " Try searching in left-to-right mode
+ set rightleftcmd=
+ call cursor(1, 1)
+ call feedkeys("/^sghl!\<C-^>vim$\<CR>", 'tx')
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ set rightleftcmd&
+ set rightleft&
+ set arabic&
+ bwipe!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index 164149476f..ca7c8574cb 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -1,5 +1,8 @@
" Test argument list commands
+source shared.vim
+source term_util.vim
+
func Reset_arglist()
args a | %argd
endfunc
@@ -510,3 +513,42 @@ func Test_argdo()
call assert_equal(['Xa.c', 'Xb.c', 'Xc.c'], l)
bwipe Xa.c Xb.c Xc.c
endfunc
+
+" Test for quiting Vim with unedited files in the argument list
+func Test_quit_with_arglist()
+ if !CanRunVimInTerminal()
+ throw 'Skipped: cannot run vim in terminal'
+ endif
+ let buf = RunVimInTerminal('', {'rows': 6})
+ call term_sendkeys(buf, ":set nomore\n")
+ call term_sendkeys(buf, ":args a b c\n")
+ call term_sendkeys(buf, ":quit\n")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_match('^E173:', term_getline(buf, 6))})
+ call StopVimInTerminal(buf)
+
+ " Try :confirm quit with unedited files in arglist
+ let buf = RunVimInTerminal('', {'rows': 6})
+ call term_sendkeys(buf, ":set nomore\n")
+ call term_sendkeys(buf, ":args a b c\n")
+ call term_sendkeys(buf, ":confirm quit\n")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_match('^\[Y\]es, (N)o: *$',
+ \ term_getline(buf, 6))})
+ call term_sendkeys(buf, "N")
+ call term_wait(buf)
+ call term_sendkeys(buf, ":confirm quit\n")
+ call WaitForAssert({-> assert_match('^\[Y\]es, (N)o: *$',
+ \ term_getline(buf, 6))})
+ call term_sendkeys(buf, "Y")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal("finished", term_getstatus(buf))})
+ only!
+ " When this test fails, swap files are left behind which breaks subsequent
+ " tests
+ call delete('.a.swp')
+ call delete('.b.swp')
+ call delete('.c.swp')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim
index 28c5948142..fdd8b0bef6 100644
--- a/src/nvim/testdir/test_assert.vim
+++ b/src/nvim/testdir/test_assert.vim
@@ -50,6 +50,26 @@ func Test_assert_equal()
call remove(v:errors, 0)
endfunc
+func Test_assert_equal_dict()
+ call assert_equal(0, assert_equal(#{one: 1, two: 2}, #{two: 2, one: 1}))
+
+ call assert_equal(1, assert_equal(#{one: 1, two: 2}, #{two: 2, one: 3}))
+ call assert_match("Expected {'one': 1} but got {'one': 3} - 1 equal item omitted", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, assert_equal(#{one: 1, two: 2}, #{two: 22, one: 11}))
+ call assert_match("Expected {'one': 1, 'two': 2} but got {'one': 11, 'two': 22}", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, assert_equal(#{}, #{two: 2, one: 1}))
+ call assert_match("Expected {} but got {'one': 1, 'two': 2}", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, assert_equal(#{two: 2, one: 1}, #{}))
+ call assert_match("Expected {'one': 1, 'two': 2} but got {}", v:errors[0])
+ call remove(v:errors, 0)
+endfunc
+
func Test_assert_equalfile()
call assert_equal(1, assert_equalfile('abcabc', 'xyzxyz'))
call assert_match("E485: Can't read file abcabc", v:errors[0])
@@ -257,6 +277,26 @@ func Test_assert_with_msg()
call remove(v:errors, 0)
endfunc
+func Test_mouse_position()
+ throw 'Skipped: Nvim does not have test_setmouse()'
+ let save_mouse = &mouse
+ set mouse=a
+ new
+ call setline(1, ['line one', 'line two'])
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+ call test_setmouse(1, 5)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 5, 0], getpos('.'))
+ call test_setmouse(2, 20)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 2, 8, 0], getpos('.'))
+ call test_setmouse(5, 1)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 2, 1, 0], getpos('.'))
+ bwipe!
+ let &mouse = save_mouse
+endfunc
+
" Must be last.
func Test_zz_quit_detected()
" Verify that if a test function ends Vim the test script detects this.
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 660801d575..438851a0ad 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -169,7 +169,9 @@ func Test_autocmd_bufunload_avoiding_SEGV_01()
exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
augroup END
- call assert_fails('edit bb.txt', 'E937:')
+ " Todo: check for E937 generated first
+ " call assert_fails('edit bb.txt', 'E937:')
+ call assert_fails('edit bb.txt', 'E517:')
autocmd! test_autocmd_bufunload
augroup! test_autocmd_bufunload
@@ -540,7 +542,7 @@ func Test_three_windows()
e Xtestje2
sp Xtestje1
call assert_fails('e', 'E937:')
- call assert_equal('Xtestje2', expand('%'))
+ call assert_equal('Xtestje1', expand('%'))
" Test changing buffers in a BufWipeout autocommand. If this goes wrong
" there are ml_line errors and/or a Crash.
@@ -563,7 +565,6 @@ func Test_three_windows()
au!
enew
- bwipe! Xtestje1
call delete('Xtestje1')
call delete('Xtestje2')
call delete('Xtestje3')
@@ -1719,7 +1720,7 @@ func Test_Cmd_Autocmds()
au BufWriteCmd XtestE call extend(g:lines, getline(0, '$'))
wall " will write other window to 'lines'
call assert_equal(4, len(g:lines), g:lines)
- call assert_equal("\tasdf", g:lines[2])
+ call assert_equal("asdf", g:lines[2])
au! BufReadCmd
au! BufWriteCmd
@@ -2225,7 +2226,7 @@ func Test_autocmd_bufreadpre()
" (even though the position will be invalid, this should make Vim reset the
" cursor position in the other window.
wincmd p
- 1
+ 1 " set cpo+=g
" won't do anything, but try to set the cursor on an invalid lnum
autocmd BufReadPre <buffer> :norm! 70gg
" triggers BufReadPre, should not move the cursor in either window
@@ -2240,8 +2241,11 @@ func Test_autocmd_bufreadpre()
close
close
call delete('XAutocmdBufReadPre.txt')
+ " set cpo-=g
endfunc
+" FileChangedShell tested in test_filechanged.vim
+
" Tests for the following autocommands:
" - FileWritePre writing a compressed file
" - FileReadPost reading a compressed file
@@ -2559,7 +2563,29 @@ func Test_BufWrite_lockmarks()
call delete('Xtest2')
endfunc
-" FileChangedShell tested in test_filechanged.vim
+" Test closing a window or editing another buffer from a FileChangedRO handler
+" in a readonly buffer
+func Test_FileChangedRO_winclose()
+ augroup FileChangedROTest
+ au!
+ autocmd FileChangedRO * quit
+ augroup END
+ new
+ set readonly
+ call assert_fails('normal i', 'E788:')
+ close
+ augroup! FileChangedROTest
+
+ augroup FileChangedROTest
+ au!
+ autocmd FileChangedRO * edit Xfile
+ augroup END
+ new
+ set readonly
+ call assert_fails('normal i', 'E788:')
+ close
+ augroup! FileChangedROTest
+endfunc
func LogACmd()
call add(g:logged, line('$'))
@@ -2668,6 +2694,27 @@ func Test_autocmd_window()
%bw!
endfunc
+" Test for trying to close the temporary window used for executing an autocmd
+func Test_close_autocmd_window()
+ %bw!
+ edit one.txt
+ tabnew two.txt
+ augroup aucmd_win_test2
+ au!
+ " Nvim makes aucmd_win the last window
+ " au BufEnter * if expand('<afile>') == 'one.txt' | 1close | endif
+ au BufEnter * if expand('<afile>') == 'one.txt' | close | endif
+ augroup END
+
+ call assert_fails('doautoall BufEnter', 'E813:')
+
+ augroup aucmd_win_test2
+ au!
+ augroup END
+ augroup! aucmd_win_test2
+ %bw!
+endfunc
+
" Test for trying to close the tab that has the temporary window for exeucing
" an autocmd.
func Test_close_autocmd_tab()
diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index 9eb768f124..67be3e6747 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -2,6 +2,161 @@
source check.vim
+" Test for the :bunload command with an offset
+func Test_bunload_with_offset()
+ %bwipe!
+ call writefile(['B1'], 'b1')
+ call writefile(['B2'], 'b2')
+ call writefile(['B3'], 'b3')
+ call writefile(['B4'], 'b4')
+
+ " Load four buffers. Unload the second and third buffers and then
+ " execute .+3bunload to unload the last buffer.
+ edit b1
+ new b2
+ new b3
+ new b4
+
+ bunload b2
+ bunload b3
+ exe bufwinnr('b1') . 'wincmd w'
+ .+3bunload
+ call assert_equal(0, getbufinfo('b4')[0].loaded)
+ call assert_equal('b1',
+ \ fnamemodify(getbufinfo({'bufloaded' : 1})[0].name, ':t'))
+
+ " Load four buffers. Unload the third and fourth buffers. Execute .+3bunload
+ " and check whether the second buffer is unloaded.
+ ball
+ bunload b3
+ bunload b4
+ exe bufwinnr('b1') . 'wincmd w'
+ .+3bunload
+ call assert_equal(0, getbufinfo('b2')[0].loaded)
+ call assert_equal('b1',
+ \ fnamemodify(getbufinfo({'bufloaded' : 1})[0].name, ':t'))
+
+ " Load four buffers. Unload the second and third buffers and from the last
+ " buffer execute .-3bunload to unload the first buffer.
+ ball
+ bunload b2
+ bunload b3
+ exe bufwinnr('b4') . 'wincmd w'
+ .-3bunload
+ call assert_equal(0, getbufinfo('b1')[0].loaded)
+ call assert_equal('b4',
+ \ fnamemodify(getbufinfo({'bufloaded' : 1})[0].name, ':t'))
+
+ " Load four buffers. Unload the first and second buffers. Execute .-3bunload
+ " from the last buffer and check whether the third buffer is unloaded.
+ ball
+ bunload b1
+ bunload b2
+ exe bufwinnr('b4') . 'wincmd w'
+ .-3bunload
+ call assert_equal(0, getbufinfo('b3')[0].loaded)
+ call assert_equal('b4',
+ \ fnamemodify(getbufinfo({'bufloaded' : 1})[0].name, ':t'))
+
+ %bwipe!
+ call delete('b1')
+ call delete('b2')
+ call delete('b3')
+ call delete('b4')
+
+ call assert_fails('1,4bunload', 'E16:')
+ call assert_fails(',100bunload', 'E16:')
+
+ " Use a try-catch for this test. When assert_fails() is used for this
+ " test, the command fails with E515: instead of E90:
+ let caught_E90 = 0
+ try
+ $bunload
+ catch /E90:/
+ let caught_E90 = 1
+ endtry
+ call assert_equal(1, caught_E90)
+ call assert_fails('$bunload', 'E515:')
+endfunc
+
+" Test for :buffer, :bnext, :bprevious, :brewind, :blast and :bmodified
+" commands
+func Test_buflist_browse()
+ %bwipe!
+ call assert_fails('buffer 1000', 'E86:')
+
+ call writefile(['foo1', 'foo2', 'foo3', 'foo4'], 'Xfile1')
+ call writefile(['bar1', 'bar2', 'bar3', 'bar4'], 'Xfile2')
+ call writefile(['baz1', 'baz2', 'baz3', 'baz4'], 'Xfile3')
+ edit Xfile1
+ let b1 = bufnr()
+ edit Xfile2
+ let b2 = bufnr()
+ edit +/baz4 Xfile3
+ let b3 = bufnr()
+
+ call assert_fails('buffer ' .. b1 .. ' abc', 'E488:')
+ call assert_equal(b3, bufnr())
+ call assert_equal(4, line('.'))
+ exe 'buffer +/bar2 ' .. b2
+ call assert_equal(b2, bufnr())
+ call assert_equal(2, line('.'))
+ exe 'buffer +/bar1'
+ call assert_equal(b2, bufnr())
+ call assert_equal(1, line('.'))
+
+ brewind +
+ call assert_equal(b1, bufnr())
+ call assert_equal(4, line('.'))
+
+ blast +/baz2
+ call assert_equal(b3, bufnr())
+ call assert_equal(2, line('.'))
+
+ bprevious +/bar4
+ call assert_equal(b2, bufnr())
+ call assert_equal(4, line('.'))
+
+ bnext +/baz3
+ call assert_equal(b3, bufnr())
+ call assert_equal(3, line('.'))
+
+ call assert_fails('bmodified', 'E84:')
+ call setbufvar(b2, '&modified', 1)
+ exe 'bmodified +/bar3'
+ call assert_equal(b2, bufnr())
+ call assert_equal(3, line('.'))
+
+ " With no listed buffers in the list, :bnext and :bprev should fail
+ %bwipe!
+ set nobuflisted
+ call assert_fails('bnext', 'E85:')
+ call assert_fails('bprev', 'E85:')
+ set buflisted
+
+ call assert_fails('sandbox bnext', 'E48:')
+
+ call delete('Xfile1')
+ call delete('Xfile2')
+ call delete('Xfile3')
+ %bwipe!
+endfunc
+
+" Test for :bdelete
+func Test_bdelete_cmd()
+ %bwipe!
+ call assert_fails('bdelete 5', 'E516:')
+ call assert_fails('1,1bdelete 1 2', 'E488:')
+
+ " Deleting a unlisted and unloaded buffer
+ edit Xfile1
+ let bnr = bufnr()
+ set nobuflisted
+ enew
+ call assert_fails('bdelete ' .. bnr, 'E516:')
+ %bwipe!
+endfunc
+
func Test_buffer_error()
new foo1
new foo2
diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim
index c364babd65..a1e53df774 100644
--- a/src/nvim/testdir/test_cd.vim
+++ b/src/nvim/testdir/test_cd.vim
@@ -225,6 +225,21 @@ func Test_cd_from_non_existing_dir()
call assert_equal(saveddir, getcwd())
endfunc
+func Test_cd_completion()
+ call mkdir('XComplDir1', 'p')
+ call mkdir('XComplDir2', 'p')
+ call writefile([], 'XComplFile')
+
+ for cmd in ['cd', 'chdir', 'lcd', 'lchdir', 'tcd', 'tchdir']
+ call feedkeys(':' .. cmd .. " XCompl\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"' .. cmd .. ' XComplDir1/ XComplDir2/', @:)
+ endfor
+
+ call delete('XComplDir1', 'd')
+ call delete('XComplDir2', 'd')
+ call delete('XComplFile')
+endfunc
+
func Test_cd_unknown_dir()
call mkdir('Xa')
cd Xa
diff --git a/src/nvim/testdir/test_changelist.vim b/src/nvim/testdir/test_changelist.vim
index ce77c1f3c7..3741f32e69 100644
--- a/src/nvim/testdir/test_changelist.vim
+++ b/src/nvim/testdir/test_changelist.vim
@@ -46,3 +46,5 @@ func Test_getchangelist()
call delete('Xfile1.txt')
call delete('Xfile2.txt')
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim
index 6f09e85a42..d386d74f8d 100644
--- a/src/nvim/testdir/test_charsearch.vim
+++ b/src/nvim/testdir/test_charsearch.vim
@@ -1,3 +1,4 @@
+" Test for character search commands - t, T, f, F, ; and ,
func Test_charsearch()
enew!
@@ -28,6 +29,17 @@ func Test_charsearch()
set cpo-=;
normal! ;;p
call assert_equal('ZabcdeZfghijkZZemnokqretkZvwxyz', getline(3))
+
+ " check that repeating a search before and after a line fails
+ normal 3Gfv
+ call assert_beeps('normal ;')
+ call assert_beeps('normal ,')
+
+ " clear the character search
+ call setcharsearch({'char' : ''})
+ call assert_equal('', getcharsearch().char)
+
+ call assert_fails("call setcharsearch([])", 'E715:')
enew!
endfunc
@@ -60,3 +72,30 @@ func Test_search_cmds()
call assert_equal('ddd yee y', getline(6))
enew!
endfunc
+
+" Test for character search in virtual edit mode with <Tab>
+func Test_csearch_virtualedit()
+ new
+ set virtualedit=all
+ call setline(1, "a\tb")
+ normal! tb
+ call assert_equal([0, 1, 2, 6], getpos('.'))
+ set virtualedit&
+ close!
+endfunc
+
+" Test for character search failure in latin1 encoding
+func Test_charsearch_latin1()
+ new
+ let save_enc = &encoding
+ " set encoding=latin1
+ call setline(1, 'abcdefghijk')
+ call assert_beeps('normal fz')
+ call assert_beeps('normal tx')
+ call assert_beeps('normal $Fz')
+ call assert_beeps('normal $Tx')
+ let &encoding = save_enc
+ close!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim
index 7ba8ef3397..ccc8168c09 100644
--- a/src/nvim/testdir/test_cindent.vim
+++ b/src/nvim/testdir/test_cindent.vim
@@ -5306,11 +5306,20 @@ func Test_cindent_case()
set cindent
norm! f:a:
call assert_equal('case x:: // x', getline(1))
-
set cindent&
bwipe!
endfunc
+" Test for changing multiple lines (using c) with cindent
+func Test_cindent_change_multline()
+ new
+ setlocal cindent
+ call setline(1, ['if (a)', '{', ' i = 1;', '}'])
+ normal! jc3jm = 2;
+ call assert_equal("\tm = 2;", getline(2))
+ close!
+endfunc
+
" This was reading past the end of the line
func Test_cindent_check_funcdecl()
new
diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim
index 922803438f..db62fe5fa6 100644
--- a/src/nvim/testdir/test_clientserver.vim
+++ b/src/nvim/testdir/test_clientserver.vim
@@ -39,6 +39,8 @@ func Test_client_server()
call remote_send(name, ":let testvar = 'yes'\<CR>")
call WaitFor('remote_expr("' . name . '", "exists(\"testvar\") ? testvar : \"\"", "", 1) == "yes"')
call assert_equal('yes', remote_expr(name, "testvar", "", 2))
+ call assert_fails("let x=remote_expr(name, '2+x')", 'E449:')
+ call assert_fails("let x=remote_expr('[], '2+2')", 'E116:')
if has('unix') && has('gui') && !has('gui_running')
" Running in a terminal and the GUI is available: Tell the server to open
@@ -75,6 +77,7 @@ func Test_client_server()
eval 'MYSELF'->remote_startserver()
" May get MYSELF1 when running the test again.
call assert_match('MYSELF', v:servername)
+ call assert_fails("call remote_startserver('MYSELF')", 'E941:')
endif
let g:testvar = 'myself'
call assert_equal('myself', remote_expr(v:servername, 'testvar'))
@@ -107,7 +110,12 @@ func Test_client_server()
call job_stop(job, 'kill')
endif
endtry
+
+ call assert_fails("let x=remote_peek([])", 'E730:')
+ call assert_fails("let x=remote_read('vim10')", 'E277:')
endfunc
" Uncomment this line to get a debugging log
" call ch_logfile('channellog', 'w')
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 8d1746be2f..276bb7fb71 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -2,6 +2,7 @@
source check.vim
source screendump.vim
+source view_util.vim
func Test_complete_tab()
call writefile(['testfile'], 'Xtestfile')
@@ -18,6 +19,11 @@ func Test_complete_list()
" We can't see the output, but at least we check the code runs properly.
call feedkeys(":e test\<C-D>\r", "tx")
call assert_equal('test', expand('%:t'))
+
+ " If a command doesn't support completion, then CTRL-D should be literally
+ " used.
+ call feedkeys(":chistory \<C-D>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"chistory \<C-D>", @:)
endfunc
func Test_complete_wildmenu()
@@ -71,6 +77,17 @@ func Test_complete_wildmenu()
cunmap <C-K>
endif
+ " Test for canceling the wild menu by adding a character
+ redrawstatus
+ call feedkeys(":e Xdir1/\<Tab>x\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xdir1/Xdir2/x', @:)
+
+ " Completion using a relative path
+ cd Xdir1/Xdir2
+ call feedkeys(":e ../\<Tab>\<Right>\<Down>\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"e Xtestfile3 Xtestfile4', @:)
+ cd -
+
" cleanup
%bwipe
call delete('Xdir1/Xdir2/Xtestfile4')
@@ -581,6 +598,29 @@ func Test_cmdline_paste()
" ignore error E32
endtry
call assert_equal("Xtestfile", bufname("%"))
+
+ " Try to paste an invalid register using <C-R>
+ call feedkeys(":\"one\<C-R>\<C-X>two\<CR>", 'xt')
+ call assert_equal('"onetwo', @:)
+
+ " Test for pasting register containing CTRL-H using CTRL-R and CTRL-R CTRL-R
+ let @a = "xy\<C-H>z"
+ call feedkeys(":\"\<C-R>a\<CR>", 'xt')
+ call assert_equal('"xz', @:)
+ call feedkeys(":\"\<C-R>\<C-R>a\<CR>", 'xt')
+ call assert_equal("\"xy\<C-H>z", @:)
+ call feedkeys(":\"\<C-R>\<C-O>a\<CR>", 'xt')
+ call assert_equal("\"xy\<C-H>z", @:)
+
+ " Test for pasting register containing CTRL-V using CTRL-R and CTRL-R CTRL-R
+ let @a = "xy\<C-V>z"
+ call feedkeys(":\"\<C-R>=@a\<CR>\<cr>", 'xt')
+ call assert_equal('"xyz', @:)
+ call feedkeys(":\"\<C-R>\<C-R>=@a\<CR>\<cr>", 'xt')
+ call assert_equal("\"xy\<C-V>z", @:)
+
+ call assert_beeps('call feedkeys(":\<C-R>=\<C-R>=\<Esc>", "xt")')
+
bwipe!
endfunc
@@ -757,6 +797,15 @@ funct Test_cmdline_complete_languages()
endif
endfunc
+func Test_cmdline_complete_env_variable()
+ let $X_VIM_TEST_COMPLETE_ENV = 'foo'
+
+ call feedkeys(":edit $X_VIM_TEST_COMPLETE_E\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('"edit $X_VIM_TEST_COMPLETE_ENV', @:)
+
+ unlet $X_VIM_TEST_COMPLETE_ENV
+endfunc
+
func Test_cmdline_complete_expression()
let g:SomeVar = 'blah'
for cmd in ['exe', 'echo', 'echon', 'echomsg']
@@ -768,6 +817,158 @@ func Test_cmdline_complete_expression()
unlet g:SomeVar
endfunc
+" Test for various command-line completion
+func Test_cmdline_complete_various()
+ " completion for a command starting with a comment
+ call feedkeys(": :|\"\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\" :|\"\<C-A>", @:)
+
+ " completion for a range followed by a comment
+ call feedkeys(":1,2\"\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"1,2\"\<C-A>", @:)
+
+ " completion for :k command
+ call feedkeys(":ka\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"ka\<C-A>", @:)
+
+ " completion for short version of the :s command
+ call feedkeys(":sI \<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"sI \<C-A>", @:)
+
+ " completion for :write command
+ call mkdir('Xdir')
+ call writefile(['one'], 'Xdir/Xfile1')
+ let save_cwd = getcwd()
+ cd Xdir
+ call feedkeys(":w >> \<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"w >> Xfile1", @:)
+ call chdir(save_cwd)
+ call delete('Xdir', 'rf')
+
+ " completion for :w ! and :r ! commands
+ call feedkeys(":w !invalid_xyz_cmd\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"w !invalid_xyz_cmd", @:)
+ call feedkeys(":r !invalid_xyz_cmd\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"r !invalid_xyz_cmd", @:)
+
+ " completion for :>> and :<< commands
+ call feedkeys(":>>>\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\">>>\<C-A>", @:)
+ call feedkeys(":<<<\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"<<<\<C-A>", @:)
+
+ " completion for command with +cmd argument
+ call feedkeys(":buffer +/pat Xabc\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"buffer +/pat Xabc", @:)
+ call feedkeys(":buffer +/pat\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"buffer +/pat\<C-A>", @:)
+
+ " completion for a command with a trailing comment
+ call feedkeys(":ls \" comment\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"ls \" comment\<C-A>", @:)
+
+ " completion for a command with a trailing command
+ call feedkeys(":ls | ls\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"ls | ls", @:)
+
+ " completion for a command with an CTRL-V escaped argument
+ call feedkeys(":ls \<C-V>\<C-V>a\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"ls \<C-V>a\<C-A>", @:)
+
+ " completion for a command that doesn't take additional arguments
+ call feedkeys(":all abc\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"all abc\<C-A>", @:)
+
+ " completion for a command with a command modifier
+ call feedkeys(":topleft new\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"topleft new", @:)
+
+ " completion for the :match command
+ call feedkeys(":match Search /pat/\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"match Search /pat/\<C-A>", @:)
+
+ " completion for the :s command
+ call feedkeys(":s/from/to/g\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"s/from/to/g\<C-A>", @:)
+
+ " completion for the :dlist command
+ call feedkeys(":dlist 10 /pat/ a\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"dlist 10 /pat/ a\<C-A>", @:)
+
+ " completion for the :doautocmd command
+ call feedkeys(":doautocmd User MyCmd a.c\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"doautocmd User MyCmd a.c\<C-A>", @:)
+
+ " completion for the :augroup command
+ augroup XTest
+ augroup END
+ call feedkeys(":augroup X\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"augroup XTest", @:)
+ augroup! XTest
+
+ " completion for the :unlet command
+ call feedkeys(":unlet one two\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"unlet one two", @:)
+
+ " completion for the :buffer command with curlies
+ " FIXME: what should happen on MS-Windows?
+ if !has('win32')
+ edit \{someFile}
+ call feedkeys(":buf someFile\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"buf {someFile}", @:)
+ bwipe {someFile}
+ endif
+
+ " completion for the :bdelete command
+ call feedkeys(":bdel a b c\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"bdel a b c", @:)
+
+ " completion for the :mapclear command
+ call feedkeys(":mapclear \<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"mapclear <buffer>", @:)
+
+ " completion for user defined commands with menu names
+ menu Test.foo :ls<CR>
+ com -nargs=* -complete=menu MyCmd
+ call feedkeys(":MyCmd Te\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd Test.', @:)
+ delcom MyCmd
+ unmenu Test
+
+ " completion for user defined commands with mappings
+ mapclear
+ map <F3> :ls<CR>
+ com -nargs=* -complete=mapping MyCmd
+ call feedkeys(":MyCmd <F\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd <F3>', @:)
+ mapclear
+ delcom MyCmd
+
+ " completion for :set path= with multiple backslashes
+ call feedkeys(":set path=a\\\\\\ b\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set path=a\\\ b', @:)
+
+ " completion for :set dir= with a backslash
+ call feedkeys(":set dir=a\\ b\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set dir=a\ b', @:)
+
+ " completion for the :py3 commands
+ call feedkeys(":py3\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"py3 py3do py3file', @:)
+
+ " redir @" is not the start of a comment. So complete after that
+ call feedkeys(":redir @\" | cwin\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"redir @" | cwindow', @:)
+
+ " completion after a backtick
+ call feedkeys(":e `a1b2c\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e `a1b2c', @:)
+
+ " completion for the expression register
+ call feedkeys(":\"\<C-R>=float2\t\"\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"float2nr("', @=)
+endfunc
+
func Test_cmdline_write_alternatefile()
new
call setline('.', ['one', 'two'])
@@ -809,9 +1010,32 @@ func Test_cmdline_search_range()
1,\&s/b/B/
call assert_equal('B', getline(2))
+ let @/ = 'apple'
+ call assert_fails('\/print', 'E486:')
+
bwipe!
endfunc
+" Test for the tick mark (') in an excmd range
+func Test_tick_mark_in_range()
+ " If only the tick is passed as a range and no command is specified, there
+ " should not be an error
+ call feedkeys(":'\<CR>", 'xt')
+ call assert_equal("'", getreg(':'))
+ call assert_fails("',print", 'E78:')
+endfunc
+
+" Test for using a line number followed by a search pattern as range
+func Test_lnum_and_pattern_as_range()
+ new
+ call setline(1, ['foo 1', 'foo 2', 'foo 3'])
+ let @" = ''
+ 2/foo/yank
+ call assert_equal("foo 3\n", @")
+ call assert_equal(1, line('.'))
+ close!
+endfunc
+
" Tests for getcmdline(), getcmdpos() and getcmdtype()
func Check_cmdline(cmdtype)
call assert_equal('MyCmd a', getcmdline())
@@ -844,6 +1068,8 @@ func Test_getcmdtype()
cnoremap <expr> <F6> Check_cmdline('=')
call feedkeys("a\<C-R>=MyCmd a\<F6>\<Esc>\<Esc>", "xt")
cunmap <F6>
+
+ call assert_equal('', getcmdline())
endfunc
func Test_getcmdwintype()
@@ -893,22 +1119,6 @@ func Test_getcmdwin_autocmd()
augroup END
endfunc
-" Test error: "E135: *Filter* Autocommands must not change current buffer"
-func Test_cmd_bang_E135()
- new
- call setline(1, ['a', 'b', 'c', 'd'])
- augroup test_cmd_filter_E135
- au!
- autocmd FilterReadPost * help
- augroup END
- call assert_fails('2,3!echo "x"', 'E135:')
-
- augroup test_cmd_filter_E135
- au!
- augroup END
- %bwipe!
-endfunc
-
func Test_verbosefile()
set verbosefile=Xlog
echomsg 'foo'
@@ -989,34 +1199,6 @@ func Test_cmdline_overstrike()
let &encoding = encoding_save
endfunc
-func Test_cmdwin_feedkeys()
- " This should not generate E488
- call feedkeys("q:\<CR>", 'x')
-endfunc
-
-" Tests for the issues fixed in 7.4.441.
-" When 'cedit' is set to Ctrl-C, opening the command window hangs Vim
-func Test_cmdwin_cedit()
- exe "set cedit=\<C-c>"
- normal! :
- call assert_equal(1, winnr('$'))
-
- let g:cmd_wintype = ''
- func CmdWinType()
- let g:cmd_wintype = getcmdwintype()
- let g:wintype = win_gettype()
- return ''
- endfunc
-
- call feedkeys("\<C-c>a\<C-R>=CmdWinType()\<CR>\<CR>")
- echo input('')
- call assert_equal('@', g:cmd_wintype)
- call assert_equal('command', g:wintype)
-
- set cedit&vim
- delfunc CmdWinType
-endfunc
-
func Test_cmdwin_restore()
CheckScreendump
@@ -1093,6 +1275,38 @@ func Test_buffers_lastused()
bwipeout bufc
endfunc
+func Test_cmdwin_feedkeys()
+ " This should not generate E488
+ call feedkeys("q:\<CR>", 'x')
+ " Using feedkeys with q: only should automatically close the cmd window
+ call feedkeys('q:', 'xt')
+ call assert_equal(1, winnr('$'))
+ call assert_equal('', getcmdwintype())
+endfunc
+
+" Tests for the issues fixed in 7.4.441.
+" When 'cedit' is set to Ctrl-C, opening the command window hangs Vim
+func Test_cmdwin_cedit()
+ exe "set cedit=\<C-c>"
+ normal! :
+ call assert_equal(1, winnr('$'))
+
+ let g:cmd_wintype = ''
+ func CmdWinType()
+ let g:cmd_wintype = getcmdwintype()
+ let g:wintype = win_gettype()
+ return ''
+ endfunc
+
+ call feedkeys("\<C-c>a\<C-R>=CmdWinType()\<CR>\<CR>")
+ echo input('')
+ call assert_equal('@', g:cmd_wintype)
+ call assert_equal('command', g:wintype)
+
+ set cedit&vim
+ delfunc CmdWinType
+endfunc
+
" Test for CmdwinEnter autocmd
func Test_cmdwin_autocmd()
CheckFeature cmdwin
@@ -1131,6 +1345,48 @@ func Test_cmdlineclear_tabenter()
call delete('XtestCmdlineClearTabenter')
endfunc
+" Test for expanding special keywords in cmdline
+func Test_cmdline_expand_special()
+ new
+ %bwipe!
+ call assert_fails('e #', 'E194:')
+ call assert_fails('e <afile>', 'E495:')
+ call assert_fails('e <abuf>', 'E496:')
+ call assert_fails('e <amatch>', 'E497:')
+ call writefile([], 'Xfile.cpp')
+ call writefile([], 'Xfile.java')
+ new Xfile.cpp
+ call feedkeys(":e %:r\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xfile.cpp Xfile.java', @:)
+ close
+ call delete('Xfile.cpp')
+ call delete('Xfile.java')
+endfunc
+
+func Test_cmdwin_jump_to_win()
+ call assert_fails('call feedkeys("q:\<C-W>\<C-W>\<CR>", "xt")', 'E11:')
+ new
+ set modified
+ call assert_fails('call feedkeys("q/:qall\<CR>", "xt")', 'E162:')
+ close!
+ call feedkeys("q/:close\<CR>", "xt")
+ call assert_equal(1, winnr('$'))
+ call feedkeys("q/:exit\<CR>", "xt")
+ call assert_equal(1, winnr('$'))
+
+ " opening command window twice should fail
+ call assert_beeps('call feedkeys("q:q:\<CR>\<CR>", "xt")')
+ call assert_equal(1, winnr('$'))
+endfunc
+
+" Test for backtick expression in the command line
+func Test_cmd_backtick()
+ %argd
+ argadd `=['a', 'b', 'c']`
+ call assert_equal(['a', 'b', 'c'], argv())
+ %argd
+endfunc
+
func Test_cmdwin_tabpage()
tabedit
" v8.2.1919 isn't ported yet, so E492 is thrown after E11 here.
@@ -1143,6 +1399,323 @@ func Test_cmdwin_tabpage()
tabclose!
endfunc
+" Test for the :! command
+func Test_cmd_bang()
+ if !has('unix')
+ return
+ endif
+
+ let lines =<< trim [SCRIPT]
+ " Test for no previous command
+ call assert_fails('!!', 'E34:')
+ set nomore
+ " Test for cmdline expansion with :!
+ call setline(1, 'foo!')
+ silent !echo <cWORD> > Xfile.out
+ call assert_equal(['foo!'], readfile('Xfile.out'))
+ " Test for using previous command
+ silent !echo \! !
+ call assert_equal(['! echo foo!'], readfile('Xfile.out'))
+ call writefile(v:errors, 'Xresult')
+ call delete('Xfile.out')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+" Test error: "E135: *Filter* Autocommands must not change current buffer"
+func Test_cmd_bang_E135()
+ new
+ call setline(1, ['a', 'b', 'c', 'd'])
+ augroup test_cmd_filter_E135
+ au!
+ autocmd FilterReadPost * help
+ augroup END
+ call assert_fails('2,3!echo "x"', 'E135:')
+
+ augroup test_cmd_filter_E135
+ au!
+ augroup END
+ %bwipe!
+endfunc
+
+" Test for using ~ for home directory in cmdline completion matches
+func Test_cmdline_expand_home()
+ call mkdir('Xdir')
+ call writefile([], 'Xdir/Xfile1')
+ call writefile([], 'Xdir/Xfile2')
+ cd Xdir
+ let save_HOME = $HOME
+ let $HOME = getcwd()
+ call feedkeys(":e ~/\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e ~/Xfile1 ~/Xfile2', @:)
+ let $HOME = save_HOME
+ cd ..
+ call delete('Xdir', 'rf')
+endfunc
+
+" Test for using CTRL-\ CTRL-G in the command line to go back to normal mode
+" or insert mode (when 'insertmode' is set)
+func Test_cmdline_ctrl_g()
+ new
+ call setline(1, 'abc')
+ call cursor(1, 3)
+ " If command line is entered from insert mode, using C-\ C-G should back to
+ " insert mode
+ call feedkeys("i\<C-O>:\<C-\>\<C-G>xy", 'xt')
+ call assert_equal('abxyc', getline(1))
+ call assert_equal(4, col('.'))
+
+ " If command line is entered in 'insertmode', using C-\ C-G should back to
+ " 'insertmode'
+ " call feedkeys(":set im\<cr>\<C-L>:\<C-\>\<C-G>12\<C-L>:set noim\<cr>", 'xt')
+ " call assert_equal('ab12xyc', getline(1))
+ close!
+endfunc
+
+" Test for 'wildmode'
+func Test_wildmode()
+ func T(a, c, p)
+ return "oneA\noneB\noneC"
+ endfunc
+ command -nargs=1 -complete=custom,T MyCmd
+
+ func SaveScreenLine()
+ let g:Sline = Screenline(&lines - 1)
+ return ''
+ endfunc
+ cnoremap <expr> <F2> SaveScreenLine()
+
+ set nowildmenu
+ set wildmode=full,list
+ let g:Sline = ''
+ call feedkeys(":MyCmd \t\t\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('oneA oneB oneC', g:Sline)
+ call assert_equal('"MyCmd oneA', @:)
+
+ set wildmode=longest,full
+ call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd one', @:)
+ call feedkeys(":MyCmd o\t\t\t\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd oneC', @:)
+
+ set wildmode=longest
+ call feedkeys(":MyCmd one\t\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd one', @:)
+
+ set wildmode=list:longest
+ let g:Sline = ''
+ call feedkeys(":MyCmd \t\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('oneA oneB oneC', g:Sline)
+ call assert_equal('"MyCmd one', @:)
+
+ set wildmode=""
+ call feedkeys(":MyCmd \t\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd oneA', @:)
+
+ " Test for wildmode=longest with 'fileignorecase' set
+ set wildmode=longest
+ set fileignorecase
+ argadd AA AAA AAAA
+ call feedkeys(":buffer \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"buffer AA', @:)
+ set fileignorecase&
+
+ " Test for listing files with wildmode=list
+ set wildmode=list
+ let g:Sline = ''
+ call feedkeys(":b A\t\t\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('AA AAA AAAA', g:Sline)
+ call assert_equal('"b A', @:)
+
+ %argdelete
+ delcommand MyCmd
+ delfunc T
+ delfunc SaveScreenLine
+ cunmap <F2>
+ set wildmode&
+ %bwipe!
+endfunc
+
+" Test for interrupting the command-line completion
+func Test_interrupt_compl()
+ func F(lead, cmdl, p)
+ if a:lead =~ 'tw'
+ call interrupt()
+ return
+ endif
+ return "one\ntwo\nthree"
+ endfunc
+ command -nargs=1 -complete=custom,F Tcmd
+
+ set nowildmenu
+ set wildmode=full
+ let interrupted = 0
+ try
+ call feedkeys(":Tcmd tw\<Tab>\<C-B>\"\<CR>", 'xt')
+ catch /^Vim:Interrupt$/
+ let interrupted = 1
+ endtry
+ call assert_equal(1, interrupted)
+
+ delcommand Tcmd
+ delfunc F
+ set wildmode&
+endfunc
+
+" Test for moving the cursor on the : command line
+func Test_cmdline_edit()
+ let str = ":one two\<C-U>"
+ let str ..= "one two\<C-W>\<C-W>"
+ let str ..= "four\<BS>\<C-H>\<Del>\<kDel>"
+ let str ..= "\<Left>five\<Right>"
+ let str ..= "\<Home>two "
+ let str ..= "\<C-Left>one "
+ let str ..= "\<C-Right> three"
+ let str ..= "\<End>\<S-Left>four "
+ let str ..= "\<S-Right> six"
+ let str ..= "\<C-B>\"\<C-E> seven\<CR>"
+ call feedkeys(str, 'xt')
+ call assert_equal("\"one two three four five six seven", @:)
+endfunc
+
+" Test for moving the cursor on the / command line in 'rightleft' mode
+func Test_cmdline_edit_rightleft()
+ CheckFeature rightleft
+ set rightleft
+ set rightleftcmd=search
+ let str = "/one two\<C-U>"
+ let str ..= "one two\<C-W>\<C-W>"
+ let str ..= "four\<BS>\<C-H>\<Del>\<kDel>"
+ let str ..= "\<Right>five\<Left>"
+ let str ..= "\<Home>two "
+ let str ..= "\<C-Right>one "
+ let str ..= "\<C-Left> three"
+ let str ..= "\<End>\<S-Right>four "
+ let str ..= "\<S-Left> six"
+ let str ..= "\<C-B>\"\<C-E> seven\<CR>"
+ call assert_fails("call feedkeys(str, 'xt')", 'E486:')
+ call assert_equal("\"one two three four five six seven", @/)
+ set rightleftcmd&
+ set rightleft&
+endfunc
+
+" Test for using <C-\>e in the command line to evaluate an expression
+func Test_cmdline_expr()
+ " Evaluate an expression from the beginning of a command line
+ call feedkeys(":abc\<C-B>\<C-\>e\"\\\"hello\"\<CR>\<CR>", 'xt')
+ call assert_equal('"hello', @:)
+
+ " Use an invalid expression for <C-\>e
+ call assert_beeps('call feedkeys(":\<C-\>einvalid\<CR>", "tx")')
+
+ " Insert literal <CTRL-\> in the command line
+ call feedkeys(":\"e \<C-\>\<C-Y>\<CR>", 'xt')
+ call assert_equal("\"e \<C-\>\<C-Y>", @:)
+endfunc
+
+" Test for 'imcmdline' and 'imsearch'
+" This test doesn't actually test the input method functionality.
+func Test_cmdline_inputmethod()
+ new
+ call setline(1, ['', 'abc', ''])
+ set imcmdline
+
+ call feedkeys(":\"abc\<CR>", 'xt')
+ call assert_equal("\"abc", @:)
+ call feedkeys(":\"\<C-^>abc\<C-^>\<CR>", 'xt')
+ call assert_equal("\"abc", @:)
+ call feedkeys("/abc\<CR>", 'xt')
+ call assert_equal([2, 1], [line('.'), col('.')])
+ call feedkeys("/\<C-^>abc\<C-^>\<CR>", 'xt')
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ " set imsearch=2
+ call cursor(1, 1)
+ call feedkeys("/abc\<CR>", 'xt')
+ call assert_equal([2, 1], [line('.'), col('.')])
+ call cursor(1, 1)
+ call feedkeys("/\<C-^>abc\<C-^>\<CR>", 'xt')
+ call assert_equal([2, 1], [line('.'), col('.')])
+ set imdisable
+ call feedkeys("/\<C-^>abc\<C-^>\<CR>", 'xt')
+ call assert_equal([2, 1], [line('.'), col('.')])
+ set imdisable&
+ set imsearch&
+
+ set imcmdline&
+ %bwipe!
+endfunc
+
+" Test for recursively getting multiple command line inputs
+func Test_cmdwin_multi_input()
+ call feedkeys(":\<C-R>=input('P: ')\<CR>\"cyan\<CR>\<CR>", 'xt')
+ call assert_equal('"cyan', @:)
+endfunc
+
+" Test for using CTRL-_ in the command line with 'allowrevins'
+func Test_cmdline_revins()
+ CheckNotMSWindows
+ CheckFeature rightleft
+ call feedkeys(":\"abc\<c-_>\<cr>", 'xt')
+ call assert_equal("\"abc\<c-_>", @:)
+ set allowrevins
+ call feedkeys(":\"abc\<c-_>xyz\<c-_>\<CR>", 'xt')
+ call assert_equal('"abcñèæ', @:)
+ set allowrevins&
+endfunc
+
+" Test for typing UTF-8 composing characters in the command line
+func Test_cmdline_composing_chars()
+ call feedkeys(":\"\<C-V>u3046\<C-V>u3099\<CR>", 'xt')
+ call assert_equal('"ã†ã‚™', @:)
+endfunc
+
+" Test for normal mode commands not supported in the cmd window
+func Test_cmdwin_blocked_commands()
+ call assert_fails('call feedkeys("q:\<C-T>\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-]>\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-^>\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:Q\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:Z\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<F1>\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>s\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>v\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>^\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>n\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>z\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>o\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>w\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>j\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>k\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>h\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>l\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>T\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>x\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>r\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>R\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>K\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>}\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>]\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>f\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>d\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>g\<CR>", "xt")', 'E11:')
+endfunc
+
+" Close the Cmd-line window in insert mode using CTRL-C
+func Test_cmdwin_insert_mode_close()
+ %bw!
+ let s = ''
+ exe "normal q:a\<C-C>let s='Hello'\<CR>"
+ call assert_equal('Hello', s)
+ call assert_equal(1, winnr('$'))
+endfunc
+
" test that ";" works to find a match at the start of the first line
func Test_zero_line_search()
new
@@ -1268,4 +1841,9 @@ func Test_recursive_register()
call assert_equal('yes', caught)
endfunc
+func Test_long_error_message()
+ " the error should be truncated, not overrun IObuff
+ silent! norm Q00000000000000     000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000                                                                                                                                                                                                                        
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim
index c7dddf4164..55b230373f 100644
--- a/src/nvim/testdir/test_command_count.vim
+++ b/src/nvim/testdir/test_command_count.vim
@@ -33,7 +33,7 @@ func Test_command_count_0()
delcommand RangeBuffers
delcommand RangeBuffersAll
- set nohidden
+ set hidden&
set swapfile&
endfunc
diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim
index c0c572ce65..3dc8710d63 100644
--- a/src/nvim/testdir/test_compiler.vim
+++ b/src/nvim/testdir/test_compiler.vim
@@ -68,5 +68,9 @@ func Test_compiler_completion()
endfunc
func Test_compiler_error()
+ let g:current_compiler = 'abc'
call assert_fails('compiler doesnotexist', 'E666:')
+ call assert_equal('abc', g:current_compiler)
+ call assert_fails('compiler! doesnotexist', 'E666:')
+ unlet! g:current_compiler
endfunc
diff --git a/src/nvim/testdir/test_cscope.vim b/src/nvim/testdir/test_cscope.vim
index faf37485cd..76ea35fa10 100644
--- a/src/nvim/testdir/test_cscope.vim
+++ b/src/nvim/testdir/test_cscope.vim
@@ -1,7 +1,11 @@
" Test for cscope commands.
-if !has('cscope') || !executable('cscope') || !has('quickfix')
- finish
+source check.vim
+CheckFeature cscope
+CheckFeature quickfix
+
+if !executable('cscope')
+ throw 'Skipped: cscope program missing'
endif
func CscopeSetupOrClean(setup)
diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim
index 6c6a6290cb..3b8a5f27ad 100644
--- a/src/nvim/testdir/test_cursor_func.vim
+++ b/src/nvim/testdir/test_cursor_func.vim
@@ -99,6 +99,7 @@ func Test_screenpos()
\ 'curscol': wincol + 9,
\ 'endcol': wincol + 9}, screenpos(winid, 2, 22))
close
+ call assert_equal({}, screenpos(999, 1, 1))
bwipe!
call assert_equal({'col': 1, 'row': 1, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1))
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index be9a77ee75..8c20c647f1 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -540,7 +540,7 @@ func Test_diffopt_hiddenoff()
bwipe!
bwipe!
- set nohidden diffopt&
+ set hidden& diffopt&
endfunc
func Test_diffoff_hidden()
@@ -577,7 +577,7 @@ func Test_diffoff_hidden()
bwipe!
bwipe!
- set nohidden diffopt&
+ set hidden& diffopt&
endfunc
func Test_setting_cursor()
diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim
index 7b6bc940d3..acc34e5e7c 100644
--- a/src/nvim/testdir/test_digraph.vim
+++ b/src/nvim/testdir/test_digraph.vim
@@ -466,10 +466,12 @@ 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)
+ call feedkeys(":\"\<c-k>Eu\<cr>", 'xt')
+ call assert_equal('"€', @:)
+
+ " Canceling a CTRL-K on the cmdline
+ call feedkeys(":\"a\<c-k>\<esc>b\<cr>", 'xt')
+ call assert_equal('"ab', @:)
endfunc
func Test_show_digraph()
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 275c8f7a15..42c77518e4 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -270,6 +270,10 @@ func Test_edit_10()
call cursor(1, 4)
call feedkeys("A\<s-home>start\<esc>", 'txin')
call assert_equal(['startdef', 'ghi'], getline(1, '$'))
+ " start select mode again with gv
+ set selectmode=cmd
+ call feedkeys('gvabc', 'xt')
+ call assert_equal('abctdef', getline(1))
set selectmode= keymodel=
bw!
endfunc
@@ -282,7 +286,7 @@ func Test_edit_11()
call cursor(2, 1)
call feedkeys("i\<c-f>int c;\<esc>", 'tnix')
call cursor(3, 1)
- call feedkeys("i/* comment */", 'tnix')
+ call feedkeys("\<Insert>/* comment */", 'tnix')
call assert_equal(['{', "\<tab>int c;", "/* comment */"], getline(1, '$'))
" added changed cindentkeys slightly
set cindent cinkeys+=*/
@@ -345,8 +349,8 @@ func Test_edit_11_indentexpr()
bw!
endfunc
+" Test changing indent in replace mode
func Test_edit_12()
- " Test changing indent in replace mode
new
call setline(1, ["\tabc", "\tdef"])
call cursor(2, 4)
@@ -385,15 +389,15 @@ func Test_edit_12()
call feedkeys("R\<c-t>\<c-t>", 'tnix')
call assert_equal(["\tabc", "\t\t\tdef"], getline(1, '$'))
call assert_equal([0, 2, 2, 0], getpos('.'))
- set et
- set sw& et&
+ set sw&
+
+ " In replace mode, after hitting enter in a line with tab characters,
+ " pressing backspace should restore the tab characters.
%d
- call setline(1, ["\t/*"])
- set formatoptions=croql
- call cursor(1, 3)
- call feedkeys("A\<cr>\<cr>/", 'tnix')
- call assert_equal(["\t/*", " *", " */"], getline(1, '$'))
- set formatoptions&
+ setlocal autoindent backspace=2
+ call setline(1, "\tone\t\ttwo")
+ exe "normal ggRred\<CR>six" .. repeat("\<BS>", 8)
+ call assert_equal(["\tone\t\ttwo"], getline(1, '$'))
bw!
endfunc
@@ -416,16 +420,49 @@ func Test_edit_13()
call assert_equal("", getline(2))
call assert_equal(" baz", getline(3))
set autoindent&
+
+ " pressing <C-U> to erase line should keep the indent with 'autoindent'
+ set backspace=2 autoindent
+ %d
+ exe "normal i\tone\<CR>three\<C-U>two"
+ call assert_equal(["\tone", "\ttwo"], getline(1, '$'))
+ set backspace& autoindent&
+
bwipe!
endfunc
+" Test for autoindent removing indent when insert mode is stopped. Some parts
+" of the code is exercised only when interactive mode is used. So use Vim in a
+" terminal.
+func Test_autoindent_remove_indent()
+ CheckRunVimInTerminal
+ let buf = RunVimInTerminal('-N Xfile', {'rows': 6, 'cols' : 20})
+ call TermWait(buf)
+ call term_sendkeys(buf, ":set autoindent\n")
+ " leaving insert mode in a new line with indent added by autoindent, should
+ " remove the indent.
+ call term_sendkeys(buf, "i\<Tab>foo\<CR>\<Esc>")
+ " Need to delay for sometime, otherwise the code in getchar.c will not be
+ " exercised.
+ call TermWait(buf, 50)
+ " when a line is wrapped and the cursor is at the start of the second line,
+ " leaving insert mode, should move the cursor back to the first line.
+ call term_sendkeys(buf, "o" .. repeat('x', 20) .. "\<Esc>")
+ " Need to delay for sometime, otherwise the code in getchar.c will not be
+ " exercised.
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, ":w\n")
+ call TermWait(buf)
+ call StopVimInTerminal(buf)
+ call assert_equal(["\tfoo", '', repeat('x', 20)], readfile('Xfile'))
+ call delete('Xfile')
+endfunc
+
func Test_edit_CR()
" Test for <CR> in insert mode
" basically only in quickfix mode ist tested, the rest
" has been taken care of by other tests
- if !has("quickfix")
- return
- endif
+ CheckFeature quickfix
botright new
call writefile(range(1, 10), 'Xqflist.txt')
call setqflist([{'filename': 'Xqflist.txt', 'lnum': 2}])
@@ -981,6 +1018,22 @@ func Test_edit_CTRL_U()
bw!
endfunc
+func Test_edit_completefunc_delete()
+ func CompleteFunc(findstart, base)
+ if a:findstart == 1
+ return col('.') - 1
+ endif
+ normal dd
+ return ['a', 'b']
+ endfunc
+ new
+ set completefunc=CompleteFunc
+ call setline(1, ['', 'abcd', ''])
+ 2d
+ call assert_fails("normal 2G$a\<C-X>\<C-U>", 'E565:')
+ bwipe!
+endfunc
+
func Test_edit_CTRL_Z()
" Ctrl-Z when insertmode is not set inserts it literally
new
@@ -1327,7 +1380,7 @@ func Test_edit_forbidden()
try
call feedkeys("ix\<esc>", 'tnix')
call assert_fails(1, 'textlock')
- catch /^Vim\%((\a\+)\)\=:E523/ " catch E523: not allowed here
+ catch /^Vim\%((\a\+)\)\=:E565/ " catch E565: not allowed here
endtry
" TODO: Might be a bug: should x really be inserted here
call assert_equal(['xa'], getline(1, '$'))
@@ -1352,7 +1405,7 @@ func Test_edit_forbidden()
try
call feedkeys("i\<c-x>\<c-u>\<esc>", 'tnix')
call assert_fails(1, 'change in complete function')
- catch /^Vim\%((\a\+)\)\=:E523/ " catch E523
+ catch /^Vim\%((\a\+)\)\=:E565/ " catch E565
endtry
delfu Complete
set completefunc=
@@ -1597,6 +1650,22 @@ func Test_edit_startinsert()
bwipe!
endfunc
+" Test for :startreplace and :startgreplace
+func Test_edit_startreplace()
+ new
+ call setline(1, 'abc')
+ call feedkeys("l:startreplace\<CR>xyz\e", 'xt')
+ call assert_equal('axyz', getline(1))
+ call feedkeys("0:startreplace!\<CR>abc\e", 'xt')
+ call assert_equal('axyzabc', getline(1))
+ call setline(1, "a\tb")
+ call feedkeys("0l:startgreplace\<CR>xyz\e", 'xt')
+ call assert_equal("axyz\tb", getline(1))
+ call feedkeys("0i\<C-R>=execute('startreplace')\<CR>12\e", 'xt')
+ call assert_equal("12axyz\tb", getline(1))
+ close!
+endfunc
+
func Test_edit_noesckeys()
CheckNotGui
new
@@ -1617,6 +1686,58 @@ func Test_edit_noesckeys()
" set esckeys
endfunc
+" Test for running an invalid ex command in insert mode using CTRL-O
+" Note that vim has a hard-coded sleep of 3 seconds. So this test will take
+" more than 3 seconds to complete.
+func Test_edit_ctrl_o_invalid_cmd()
+ new
+ set showmode showcmd
+ let caught_e492 = 0
+ try
+ call feedkeys("i\<C-O>:invalid\<CR>abc\<Esc>", "xt")
+ catch /E492:/
+ let caught_e492 = 1
+ endtry
+ call assert_equal(1, caught_e492)
+ call assert_equal('abc', getline(1))
+ set showmode& showcmd&
+ close!
+endfunc
+
+" Test for inserting text in a line with only spaces ('H' flag in 'cpoptions')
+func Test_edit_cpo_H()
+ throw 'Skipped: Nvim does not support cpoptions flag "H"'
+ new
+ call setline(1, ' ')
+ normal! Ia
+ call assert_equal(' a', getline(1))
+ set cpo+=H
+ call setline(1, ' ')
+ normal! Ia
+ call assert_equal(' a ', getline(1))
+ set cpo-=H
+ close!
+endfunc
+
+" Test for inserting tab in virtual replace mode ('L' flag in 'cpoptions')
+func Test_edit_cpo_L()
+ new
+ call setline(1, 'abcdefghijklmnopqr')
+ exe "normal 0gR\<Tab>"
+ call assert_equal("\<Tab>ijklmnopqr", getline(1))
+ set cpo+=L
+ set list
+ call setline(1, 'abcdefghijklmnopqr')
+ exe "normal 0gR\<Tab>"
+ call assert_equal("\<Tab>cdefghijklmnopqr", getline(1))
+ set nolist
+ call setline(1, 'abcdefghijklmnopqr')
+ exe "normal 0gR\<Tab>"
+ call assert_equal("\<Tab>ijklmnopqr", getline(1))
+ set cpo-=L
+ %bw!
+endfunc
+
" Test for editing a directory
func Test_edit_is_a_directory()
CheckEnglish
diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim
index 0ce333fa40..122572f32a 100644
--- a/src/nvim/testdir/test_ex_mode.vim
+++ b/src/nvim/testdir/test_ex_mode.vim
@@ -64,16 +64,118 @@ func Test_ex_mode()
let &encoding = encoding_save
endfunc
+" Test substitute confirmation prompt :%s/pat/str/c in Ex mode
+func Test_Ex_substitute()
+ CheckRunVimInTerminal
+ let buf = RunVimInTerminal('', {'rows': 6})
+
+ call term_sendkeys(buf, ":call setline(1, ['foo foo', 'foo foo', 'foo foo'])\<CR>")
+ call term_sendkeys(buf, ":set number\<CR>")
+ call term_sendkeys(buf, "gQ")
+ call WaitForAssert({-> assert_match(':', term_getline(buf, 6))}, 1000)
+
+ call term_sendkeys(buf, "%s/foo/bar/gc\<CR>")
+ call WaitForAssert({-> assert_match(' 1 foo foo', term_getline(buf, 5))},
+ \ 1000)
+ call WaitForAssert({-> assert_match(' ^^^', term_getline(buf, 6))}, 1000)
+ call term_sendkeys(buf, "N\<CR>")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_match(' ^^^', term_getline(buf, 6))}, 1000)
+ call term_sendkeys(buf, "n\<CR>")
+ call WaitForAssert({-> assert_match(' ^^^', term_getline(buf, 6))},
+ \ 1000)
+ call term_sendkeys(buf, "y\<CR>")
+
+ call term_sendkeys(buf, "q\<CR>")
+ call WaitForAssert({-> assert_match(':', term_getline(buf, 6))}, 1000)
+
+ " Pressing enter in ex mode should print the current line
+ call term_sendkeys(buf, "\<CR>")
+ call WaitForAssert({-> assert_match(' 3 foo foo',
+ \ term_getline(buf, 5))}, 1000)
+
+ call term_sendkeys(buf, ":vi\<CR>")
+ call WaitForAssert({-> assert_match('foo bar', term_getline(buf, 1))}, 1000)
+
+ call term_sendkeys(buf, ":q!\n")
+ call StopVimInTerminal(buf)
+endfunc
+
+" Test for displaying lines from an empty buffer in Ex mode
+func Test_Ex_emptybuf()
+ new
+ call assert_fails('call feedkeys("Q\<CR>", "xt")', 'E749:')
+ call setline(1, "abc")
+ call assert_fails('call feedkeys("Q\<CR>", "xt")', 'E501:')
+ call assert_fails('call feedkeys("Q%d\<CR>", "xt")', 'E749:')
+ close!
+endfunc
+
+" Test for the :open command
+func Test_open_command()
+ throw 'Skipped: Nvim does not have :open'
+ new
+ call setline(1, ['foo foo', 'foo bar', 'foo baz'])
+ call feedkeys("Qopen\<CR>j", 'xt')
+ call assert_equal('foo bar', getline('.'))
+ call feedkeys("Qopen /bar/\<CR>", 'xt')
+ call assert_equal(5, col('.'))
+ call assert_fails('call feedkeys("Qopen /baz/\<CR>", "xt")', 'E479:')
+ close!
+endfunc
+
+" Test for :g/pat/visual to run vi commands in Ex mode
+" This used to hang Vim before 8.2.0274.
+func Test_Ex_global()
+ new
+ call setline(1, ['', 'foo', 'bar', 'foo', 'bar', 'foo'])
+ call feedkeys("Q\<bs>g/bar/visual\<CR>$rxQ$ryQvisual\<CR>j", "xt")
+ call assert_equal('bax', getline(3))
+ call assert_equal('bay', getline(5))
+ bwipe!
+endfunc
+
+" In Ex-mode, a backslash escapes a newline
+func Test_Ex_escape_enter()
+ call feedkeys("gQlet l = \"a\\\<kEnter>b\"\<cr>vi\<cr>", 'xt')
+ call assert_equal("a\rb", l)
+endfunc
+
+" Test for :append! command in Ex mode
+func Test_Ex_append()
+ throw 'Skipped: Nvim only supports Vim Ex mode'
+ new
+ call setline(1, "\t abc")
+ call feedkeys("Qappend!\npqr\nxyz\n.\nvisual\n", 'xt')
+ call assert_equal(["\t abc", "\t pqr", "\t xyz"], getline(1, '$'))
+ close!
+endfunc
+
+" In Ex-mode, backslashes at the end of a command should be halved.
+func Test_Ex_echo_backslash()
+ throw 'Skipped: Nvim only supports Vim Ex mode'
+ " This test works only when the language is English
+ if v:lang != "C" && v:lang !~ '^[Ee]n'
+ return
+ endif
+ let bsl = '\\\\'
+ let bsl2 = '\\\'
+ call assert_fails('call feedkeys("Qecho " .. bsl .. "\nvisual\n", "xt")',
+ \ "E15: Invalid expression: \\\\")
+ call assert_fails('call feedkeys("Qecho " .. bsl2 .. "\nm\nvisual\n", "xt")',
+ \ "E15: Invalid expression: \\\nm")
+endfunc
+
func Test_ex_mode_errors()
" Not allowed to enter ex mode when text is locked
au InsertCharPre <buffer> normal! gQ<CR>
- let caught_e523 = 0
+ let caught_e565 = 0
try
call feedkeys("ix\<esc>", 'xt')
- catch /^Vim\%((\a\+)\)\=:E523/ " catch E523
- let caught_e523 = 1
+ catch /^Vim\%((\a\+)\)\=:E565/ " catch E565
+ let caught_e565 = 1
endtry
- call assert_equal(1, caught_e523)
+ call assert_equal(1, caught_e565)
au! InsertCharPre
new
@@ -89,6 +191,9 @@ func Test_ex_mode_errors()
endfunc
func Test_ex_mode_count_overflow()
+ " The multiplication causes an integer overflow
+ CheckNotAsan
+
" this used to cause a crash
let lines =<< trim END
call feedkeys("\<Esc>gQ\<CR>")
diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim
index 7dde8a0439..7d581d5efd 100644
--- a/src/nvim/testdir/test_excmd.vim
+++ b/src/nvim/testdir/test_excmd.vim
@@ -22,6 +22,7 @@ func Test_range_error()
call assert_fails(':\/echo 1', 'E481:')
normal vv
call assert_fails(":'<,'>echo 1", 'E481:')
+ call assert_fails(":\\xcenter", 'E10:')
endfunc
func Test_buffers_lastused()
@@ -65,6 +66,10 @@ func Test_copy()
1,3copy 2
call assert_equal(['L1', 'L2', 'L1', 'L2', 'L3', 'L3', 'L4'], getline(1, 7))
+ " Specifying a count before using : to run an ex-command
+ exe "normal! gg4:yank\<CR>"
+ call assert_equal("L1\nL2\nL1\nL2\n", @")
+
close!
endfunc
@@ -147,7 +152,6 @@ endfunc
" Test for the :insert command
func Test_insert_cmd()
- set noautoindent " test assumes noautoindent, but it's on by default in Nvim
new
call setline(1, [' L1'])
call feedkeys(":insert\<CR> L2\<CR> L3\<CR>.\<CR>", 'xt')
@@ -197,7 +201,6 @@ endfunc
" Test for the :change command
func Test_change_cmd()
- set noautoindent " test assumes noautoindent, but it's on by default in Nvim
new
call setline(1, [' L1', 'L2', 'L3'])
call feedkeys(":change\<CR> L4\<CR> L5\<CR>.\<CR>", 'xt')
@@ -224,12 +227,24 @@ func Test_change_cmd()
close!
endfunc
+" Test for the :language command
+func Test_language_cmd()
+ CheckNotMSWindows " FIXME: why does this fail on Windows CI?
+ CheckNotBSD " FIXME: why does this fail on OpenBSD CI?
+ CheckFeature multi_lang
+
+ call assert_fails('language ctype non_existing_lang', 'E197:')
+ call assert_fails('language time non_existing_lang', 'E197:')
+endfunc
+
" Test for the :confirm command dialog
func Test_confirm_cmd()
CheckNotGui
CheckRunVimInTerminal
+
call writefile(['foo1'], 'foo')
call writefile(['bar1'], 'bar')
+
" Test for saving all the modified buffers
let buf = RunVimInTerminal('', {'rows': 20})
call term_sendkeys(buf, ":set nomore\n")
@@ -242,8 +257,10 @@ func Test_confirm_cmd()
call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000)
call term_sendkeys(buf, "A")
call StopVimInTerminal(buf)
+
call assert_equal(['foo2'], readfile('foo'))
call assert_equal(['bar2'], readfile('bar'))
+
" Test for discarding all the changes to modified buffers
let buf = RunVimInTerminal('', {'rows': 20})
call term_sendkeys(buf, ":set nomore\n")
@@ -256,8 +273,10 @@ func Test_confirm_cmd()
call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000)
call term_sendkeys(buf, "D")
call StopVimInTerminal(buf)
+
call assert_equal(['foo2'], readfile('foo'))
call assert_equal(['bar2'], readfile('bar'))
+
" Test for saving and discarding changes to some buffers
let buf = RunVimInTerminal('', {'rows': 20})
call term_sendkeys(buf, ":set nomore\n")
@@ -272,6 +291,7 @@ func Test_confirm_cmd()
call WaitForAssert({-> assert_match('\[Y\]es, (N)o, (C)ancel: ', term_getline(buf, 20))}, 1000)
call term_sendkeys(buf, "Y")
call StopVimInTerminal(buf)
+
call assert_equal(['foo4'], readfile('foo'))
call assert_equal(['bar2'], readfile('bar'))
@@ -393,6 +413,11 @@ func Test_confirm_write_partial_file()
call delete('Xscript')
endfunc
+" Test for the :print command
+func Test_print_cmd()
+ call assert_fails('print', 'E749:')
+endfunc
+
" Test for the :winsize command
func Test_winsize_cmd()
call assert_fails('winsize 1', 'E465:')
@@ -401,6 +426,197 @@ func Test_winsize_cmd()
" Actually changing the window size would be flaky.
endfunc
+" Test for the :redir command
+func Test_redir_cmd()
+ call assert_fails('redir @@', 'E475:')
+ call assert_fails('redir abc', 'E475:')
+ if has('unix')
+ call mkdir('Xdir')
+ call assert_fails('redir > Xdir', 'E17:')
+ call delete('Xdir', 'd')
+ endif
+ if !has('bsd')
+ call writefile([], 'Xfile')
+ call setfperm('Xfile', 'r--r--r--')
+ call assert_fails('redir! > Xfile', 'E190:')
+ call delete('Xfile')
+ endif
+
+ " Test for redirecting to a register
+ redir @q> | echon 'clean ' | redir END
+ redir @q>> | echon 'water' | redir END
+ call assert_equal('clean water', @q)
+
+ " Test for redirecting to a variable
+ redir => color | echon 'blue ' | redir END
+ redir =>> color | echon 'sky' | redir END
+ call assert_equal('blue sky', color)
+endfunc
+
+" Test for the :filetype command
+func Test_filetype_cmd()
+ call assert_fails('filetype abc', 'E475:')
+endfunc
+
+" Test for the :mode command
+func Test_mode_cmd()
+ call assert_fails('mode abc', 'E359:')
+endfunc
+
+" Test for the :sleep command
+func Test_sleep_cmd()
+ call assert_fails('sleep x', 'E475:')
+endfunc
+
+" Test for the :read command
+func Test_read_cmd()
+ call writefile(['one'], 'Xfile')
+ new
+ call assert_fails('read', 'E32:')
+ edit Xfile
+ read
+ call assert_equal(['one', 'one'], getline(1, '$'))
+ close!
+ new
+ read Xfile
+ call assert_equal(['', 'one'], getline(1, '$'))
+ call deletebufline('', 1, '$')
+ call feedkeys("Qr Xfile\<CR>visual\<CR>", 'xt')
+ call assert_equal(['one'], getline(1, '$'))
+ close!
+ call delete('Xfile')
+endfunc
+
+" Test for running Ex commands when text is locked.
+" <C-\>e in the command line is used to lock the text
+func Test_run_excmd_with_text_locked()
+ " :quit
+ let cmd = ":\<C-\>eexecute('quit')\<CR>\<C-C>"
+ call assert_fails("call feedkeys(cmd, 'xt')", 'E565:')
+
+ " :qall
+ let cmd = ":\<C-\>eexecute('qall')\<CR>\<C-C>"
+ call assert_fails("call feedkeys(cmd, 'xt')", 'E565:')
+
+ " :exit
+ let cmd = ":\<C-\>eexecute('exit')\<CR>\<C-C>"
+ call assert_fails("call feedkeys(cmd, 'xt')", 'E565:')
+
+ " :close - should be ignored
+ new
+ let cmd = ":\<C-\>eexecute('close')\<CR>\<C-C>"
+ call assert_equal(2, winnr('$'))
+ close
+
+ call assert_fails("call feedkeys(\":\<C-R>=execute('bnext')\<CR>\", 'xt')", 'E565:')
+
+ " :tabfirst
+ tabnew
+ call assert_fails("call feedkeys(\":\<C-R>=execute('tabfirst')\<CR>\", 'xt')", 'E565:')
+ tabclose
+endfunc
+
+" Test for the :verbose command
+func Test_verbose_cmd()
+ call assert_equal([' verbose=1'], split(execute('verbose set vbs'), "\n"))
+ call assert_equal([' verbose=0'], split(execute('0verbose set vbs'), "\n"))
+ let l = execute("4verbose set verbose | set verbose")
+ call assert_equal([' verbose=4', ' verbose=0'], split(l, "\n"))
+endfunc
+
+" Test for the :delete command and the related abbreviated commands
+func Test_excmd_delete()
+ new
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal(['^Ibar$'], split(execute('dl'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal(['^Ibar$'], split(execute('dell'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal(['^Ibar$'], split(execute('delel'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal(['^Ibar$'], split(execute('deletl'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal(['^Ibar$'], split(execute('deletel'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('dp'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('dep'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('delp'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('delep'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('deletp'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('deletep'), "\n"))
+ close!
+endfunc
+
+" Test for commands that are blocked in a sandbox
+func Sandbox_tests()
+ call assert_fails("call histadd(':', 'ls')", 'E48:')
+ call assert_fails("call mkdir('Xdir')", 'E48:')
+ call assert_fails("call rename('a', 'b')", 'E48:')
+ call assert_fails("call setbufvar(1, 'myvar', 1)", 'E48:')
+ call assert_fails("call settabvar(1, 'myvar', 1)", 'E48:')
+ call assert_fails("call settabwinvar(1, 1, 'myvar', 1)", 'E48:')
+ call assert_fails("call setwinvar(1, 'myvar', 1)", 'E48:')
+ call assert_fails("call timer_start(100, '')", 'E48:')
+ if has('channel')
+ call assert_fails("call prompt_setcallback(1, '')", 'E48:')
+ call assert_fails("call prompt_setinterrupt(1, '')", 'E48:')
+ call assert_fails("call prompt_setprompt(1, '')", 'E48:')
+ endif
+ call assert_fails("let $TESTVAR=1", 'E48:')
+ call assert_fails("call feedkeys('ivim')", 'E48:')
+ call assert_fails("source! Xfile", 'E48:')
+ call assert_fails("call delete('Xfile')", 'E48:')
+ call assert_fails("call writefile([], 'Xfile')", 'E48:')
+ call assert_fails('!ls', 'E48:')
+ " call assert_fails('shell', 'E48:')
+ call assert_fails('stop', 'E48:')
+ call assert_fails('exe "normal \<C-Z>"', 'E48:')
+ " set insertmode
+ " call assert_fails('call feedkeys("\<C-Z>", "xt")', 'E48:')
+ " set insertmode&
+ call assert_fails('suspend', 'E48:')
+ call assert_fails('call system("ls")', 'E48:')
+ call assert_fails('call systemlist("ls")', 'E48:')
+ if has('clientserver')
+ call assert_fails('let s=remote_expr("gvim", "2+2")', 'E48:')
+ if !has('win32')
+ " remote_foreground() doesn't thrown an error message on MS-Windows
+ call assert_fails('call remote_foreground("gvim")', 'E48:')
+ endif
+ call assert_fails('let s=remote_peek("gvim")', 'E48:')
+ call assert_fails('let s=remote_read("gvim")', 'E48:')
+ call assert_fails('let s=remote_send("gvim", "abc")', 'E48:')
+ call assert_fails('let s=server2client("gvim", "abc")', 'E48:')
+ endif
+ if has('terminal')
+ call assert_fails('terminal', 'E48:')
+ call assert_fails('call term_start("vim")', 'E48:')
+ call assert_fails('call term_dumpwrite(1, "Xfile")', 'E48:')
+ endif
+ if has('channel')
+ call assert_fails("call ch_logfile('chlog')", 'E48:')
+ call assert_fails("call ch_open('localhost:8765')", 'E48:')
+ endif
+ if has('job')
+ call assert_fails("call job_start('vim')", 'E48:')
+ endif
+ if has('unix') && has('libcall')
+ call assert_fails("echo libcall('libc.so', 'getenv', 'HOME')", 'E48:')
+ endif
+ if has('unix')
+ call assert_fails('cd `pwd`', 'E48:')
+ endif
+endfunc
+
+func Test_sandbox()
+ sandbox call Sandbox_tests()
+endfunc
+
func Test_not_break_expression_register()
call setreg('=', '1+1')
if 0
diff --git a/src/nvim/testdir/test_exists.vim b/src/nvim/testdir/test_exists.vim
index fd34be83b0..471c77853d 100644
--- a/src/nvim/testdir/test_exists.vim
+++ b/src/nvim/testdir/test_exists.vim
@@ -94,8 +94,12 @@ func Test_exists()
call assert_equal(0, exists(':edit/a'))
" Valid internal command (partial match)
call assert_equal(1, exists(':q'))
+ " Valid internal command with a digit
+ call assert_equal(2, exists(':2match'))
" Non-existing internal command
call assert_equal(0, exists(':invalidcmd'))
+ " Internal command with a count
+ call assert_equal(0, exists(':3buffer'))
" User defined command (full match)
command! MyCmd :echo 'My command'
diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim
index 48dce25bb3..d86fea4f45 100644
--- a/src/nvim/testdir/test_expand.vim
+++ b/src/nvim/testdir/test_expand.vim
@@ -1,5 +1,7 @@
" Test for expanding file names
+source shared.vim
+
func Test_with_directories()
call mkdir('Xdir1')
call mkdir('Xdir2')
@@ -79,5 +81,48 @@ func Test_expandcmd()
call assert_fails('call expandcmd("make <afile>")', 'E495:')
enew
call assert_fails('call expandcmd("make %")', 'E499:')
- close
+ let $FOO="blue\tsky"
+ call setline(1, "$FOO")
+ call assert_equal("grep pat blue\tsky", expandcmd('grep pat <cfile>'))
+ unlet $FOO
+ close!
+endfunc
+
+" Test for expanding <sfile>, <slnum> and <sflnum> outside of sourcing a script
+func Test_source_sfile()
+ let lines =<< trim [SCRIPT]
+ :call assert_fails('echo expandcmd("<sfile>")', 'E498:')
+ :call assert_fails('echo expandcmd("<slnum>")', 'E842:')
+ :call assert_fails('echo expandcmd("<sflnum>")', 'E961:')
+ :call assert_fails('call expandcmd("edit <cfile>")', 'E446:')
+ :call assert_fails('call expandcmd("edit #")', 'E194:')
+ :call assert_fails('call expandcmd("edit #<2")', 'E684:')
+ :call assert_fails('call expandcmd("edit <cword>")', 'E348:')
+ :call assert_fails('call expandcmd("edit <cexpr>")', 'E348:')
+ :call assert_fails('autocmd User MyCmd echo "<sfile>"', 'E498:')
+ :call writefile(v:errors, 'Xresult')
+ :qall!
+
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -s Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+" Test for expanding filenames multiple times in a command line
+func Test_expand_filename_multicmd()
+ edit foo
+ call setline(1, 'foo!')
+ new
+ call setline(1, 'foo!')
+ new <cword> | new <cWORD>
+ call assert_equal(4, winnr('$'))
+ call assert_equal('foo!', bufname(winbufnr(1)))
+ call assert_equal('foo', bufname(winbufnr(2)))
+ %bwipe!
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim
index 8e1cb2c3ee..c6e781a1ef 100644
--- a/src/nvim/testdir/test_filechanged.vim
+++ b/src/nvim/testdir/test_filechanged.vim
@@ -237,7 +237,7 @@ func Test_file_changed_dialog()
sleep 2
silent !touch Xchanged_d
let v:warningmsg = ''
- checktime
+ checktime Xchanged_d
call assert_equal('', v:warningmsg)
call assert_equal(1, line('$'))
call assert_equal('new line', getline(1))
@@ -245,3 +245,19 @@ func Test_file_changed_dialog()
bwipe!
call delete('Xchanged_d')
endfunc
+
+" Test for editing a new buffer from a FileChangedShell autocmd
+func Test_FileChangedShell_newbuf()
+ call writefile(['one', 'two'], 'Xfile')
+ new Xfile
+ augroup testnewbuf
+ autocmd FileChangedShell * enew
+ augroup END
+ sleep 10m " make the test less flaky in Nvim
+ call writefile(['red'], 'Xfile')
+ call assert_fails('checktime', 'E811:')
+ au! testnewbuf
+ call delete('Xfile')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 88c354a55b..59b272d563 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -83,6 +83,7 @@ let s:filename_checks = {
\ 'bib': ['file.bib'],
\ 'bicep': ['file.bicep'],
\ 'bindzone': ['named.root', '/bind/db.file', '/named/db.file', 'any/bind/db.file', 'any/named/db.file'],
+ \ 'bitbake': ['file.bb', 'file.bbappend', 'file.bbclass', 'build/conf/local.conf', 'meta/conf/layer.conf', 'build/conf/bbappend.conf', 'meta-layer/conf/distro/foo.conf'],
\ 'blank': ['file.bl'],
\ 'bsdl': ['file.bsd', 'file.bsdl', 'bsd', 'some-bsd'],
\ 'bst': ['file.bst'],
@@ -115,7 +116,7 @@ let s:filename_checks = {
\ 'coco': ['file.atg'],
\ 'conaryrecipe': ['file.recipe'],
\ 'conf': ['auto.master'],
- \ 'config': ['configure.in', 'configure.ac', '/etc/hostname.file'],
+ \ 'config': ['configure.in', 'configure.ac', '/etc/hostname.file', 'any/etc/hostname.file'],
\ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf'],
\ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'],
\ 'cook': ['file.cook'],
@@ -128,6 +129,7 @@ let s:filename_checks = {
\ 'csp': ['file.csp', 'file.fdr'],
\ 'css': ['file.css'],
\ 'cterm': ['file.con'],
+ \ 'csv': ['file.csv'],
\ 'cucumber': ['file.feature'],
\ 'cuda': ['file.cu', 'file.cuh'],
\ 'cupl': ['file.pld'],
@@ -208,7 +210,7 @@ let s:filename_checks = {
\ 'gemtext': ['file.gmi', 'file.gemini'],
\ 'gift': ['file.gift'],
\ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG', 'NOTES_EDITMSG', 'EDIT_DESCRIPTION'],
- \ 'gitconfig': ['file.git/config', 'file.git/config.worktree', 'file.git/worktrees/x/config.worktree', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/usr/local/etc/gitconfig', '/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'],
+ \ 'gitconfig': ['file.git/config', 'file.git/config.worktree', 'file.git/worktrees/x/config.worktree', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/usr/local/etc/gitconfig', '/etc/gitconfig.d/file', 'any/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'],
\ 'gitolite': ['gitolite.conf', '/gitolite-admin/conf/file', 'any/gitolite-admin/conf/file'],
\ 'gitrebase': ['git-rebase-todo'],
\ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'],
@@ -313,7 +315,6 @@ let s:filename_checks = {
\ 'lotos': ['file.lot', 'file.lotos'],
\ 'lout': ['file.lou', 'file.lout'],
\ 'lpc': ['file.lpc', 'file.ulpc'],
- \ 'lprolog': ['file.sig'],
\ 'lsl': ['file.lsl'],
\ 'lss': ['file.lss'],
\ 'lua': ['file.lua', 'file.rockspec', 'file.nse'],
@@ -564,6 +565,7 @@ let s:filename_checks = {
\ 'tsscl': ['file.tsscl'],
\ 'tssgm': ['file.tssgm'],
\ 'tssop': ['file.tssop'],
+ \ 'tsv': ['file.tsv'],
\ 'twig': ['file.twig'],
\ 'typescript.glimmer': ['file.gts'],
\ 'typescriptreact': ['file.tsx'],
@@ -706,7 +708,8 @@ let s:script_checks = {
\ 'awk': [['#!/path/awk'],
\ ['#!/path/gawk']],
\ 'wml': [['#!/path/wml']],
- \ 'scheme': [['#!/path/scheme']],
+ \ 'scheme': [['#!/path/scheme'],
+ \ ['#!/path/guile']],
\ 'cfengine': [['#!/path/cfengine']],
\ 'erlang': [['#!/path/escript']],
\ 'haskell': [['#!/path/haskell']],
@@ -755,6 +758,30 @@ func Test_setfiletype_completion()
call assert_equal('"setfiletype java javacc javascript javascriptreact', @:)
endfunc
+" Test for ':filetype detect' command for a buffer without a file
+func Test_emptybuf_ftdetect()
+ new
+ call setline(1, '#!/bin/sh')
+ call assert_equal('', &filetype)
+ filetype detect
+ call assert_equal('sh', &filetype)
+ close!
+endfunc
+
+" Test for ':filetype indent on' and ':filetype indent off' commands
+func Test_filetype_indent_off()
+ new Xtest.vim
+ filetype indent on
+ call assert_equal(1, g:did_indent_on)
+ call assert_equal(['filetype detection:ON plugin:OFF indent:ON'],
+ \ execute('filetype')->split("\n"))
+ filetype indent off
+ call assert_equal(0, exists('g:did_indent_on'))
+ call assert_equal(['filetype detection:ON plugin:OFF indent:OFF'],
+ \ execute('filetype')->split("\n"))
+ close
+endfunc
+
"""""""""""""""""""""""""""""""""""""""""""""""""
" Tests for specific extensions and filetypes.
" Keep sorted.
@@ -1733,4 +1760,112 @@ func Test_cls_file()
filetype off
endfunc
+func Test_sig_file()
+ filetype on
+
+ call writefile(['this is neither Lambda Prolog nor SML'], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('', &filetype)
+ bwipe!
+
+ " Test dist#ft#FTsig()
+
+ let g:filetype_sig = 'sml'
+ split Xfile.sig
+ call assert_equal('sml', &filetype)
+ bwipe!
+ unlet g:filetype_sig
+
+ " Lambda Prolog
+
+ call writefile(['sig foo.'], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('lprolog', &filetype)
+ bwipe!
+
+ call writefile(['/* ... */'], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('lprolog', &filetype)
+ bwipe!
+
+ call writefile(['% ...'], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('lprolog', &filetype)
+ bwipe!
+
+ " SML signature file
+
+ call writefile(['signature FOO ='], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('sml', &filetype)
+ bwipe!
+
+ call writefile(['structure FOO ='], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('sml', &filetype)
+ bwipe!
+
+ call writefile(['(* ... *)'], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('sml', &filetype)
+ bwipe!
+
+ call delete('Xfile.sig')
+ filetype off
+endfunc
+
+func Test_inc_file()
+ filetype on
+
+ call writefile(['this is the fallback'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('pov', &filetype)
+ bwipe!
+
+ let g:filetype_inc = 'foo'
+ split Xfile.inc
+ call assert_equal('foo', &filetype)
+ bwipe!
+ unlet g:filetype_inc
+
+ " aspperl
+ call writefile(['perlscript'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('aspperl', &filetype)
+ bwipe!
+
+ " aspvbs
+ call writefile(['<% something'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('aspvbs', &filetype)
+ bwipe!
+
+ " php
+ call writefile(['<?php'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('php', &filetype)
+ bwipe!
+
+ " pascal
+ call writefile(['program'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('pascal', &filetype)
+ bwipe!
+
+ " bitbake
+ call writefile(['require foo'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('bitbake', &filetype)
+ bwipe!
+
+ " asm
+ call writefile(['asmsyntax=bar'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('bar', &filetype)
+ bwipe!
+
+ call delete('Xfile.inc')
+ filetype off
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_filetype_lua.vim b/src/nvim/testdir/test_filetype_lua.vim
deleted file mode 100644
index d9c0dcba9c..0000000000
--- a/src/nvim/testdir/test_filetype_lua.vim
+++ /dev/null
@@ -1,3 +0,0 @@
-let g:do_filetype_lua = 1
-let g:did_load_filetypes = 0
-source test_filetype.vim
diff --git a/src/nvim/testdir/test_filter_cmd.vim b/src/nvim/testdir/test_filter_cmd.vim
index d465e48c7b..dae164b11c 100644
--- a/src/nvim/testdir/test_filter_cmd.vim
+++ b/src/nvim/testdir/test_filter_cmd.vim
@@ -45,6 +45,14 @@ func Test_filter_fails()
call assert_fails('filter /pat', 'E476:')
call assert_fails('filter /pat/', 'E476:')
call assert_fails('filter /pat/ asdf', 'E492:')
+ " Using assert_fails() causes E476 instead of E866. So use a try-catch.
+ let caught_e866 = 0
+ try
+ filter /\@>b/ ls
+ catch /E866:/
+ let caught_e866 = 1
+ endtry
+ call assert_equal(1, caught_e866)
call assert_fails('filter!', 'E471:')
call assert_fails('filter! pat', 'E476:')
diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim
index 1684c5d30a..0f4b30aec2 100644
--- a/src/nvim/testdir/test_findfile.vim
+++ b/src/nvim/testdir/test_findfile.vim
@@ -193,12 +193,14 @@ func Test_find_cmd()
set path=.,./**/*
call CreateFiles()
cd Xdir1
+
" Test for :find
find foo
call assert_equal('foo', expand('%:.'))
2find foo
call assert_equal('Xdir2/foo', expand('%:.'))
call assert_fails('3find foo', 'E347:')
+
" Test for :sfind
enew
sfind barfoo
@@ -207,6 +209,7 @@ func Test_find_cmd()
close
call assert_fails('sfind baz', 'E345:')
call assert_equal(2, winnr('$'))
+
" Test for :tabfind
enew
tabfind foobar
@@ -215,7 +218,8 @@ func Test_find_cmd()
tabclose
call assert_fails('tabfind baz', 'E345:')
call assert_equal(1, tabpagenr('$'))
- " call chdir(save_dir)
+
+ call chdir(save_dir)
exe 'cd ' . save_dir
call CleanFiles()
let &path = save_path
diff --git a/src/nvim/testdir/test_float_func.vim b/src/nvim/testdir/test_float_func.vim
index 1e0c75c49d..902a011a9d 100644
--- a/src/nvim/testdir/test_float_func.vim
+++ b/src/nvim/testdir/test_float_func.vim
@@ -1,8 +1,7 @@
" test float functions
-if !has('float')
- finish
-end
+source check.vim
+CheckFeature float
func Test_abs()
call assert_equal('1.23', string(abs(1.23)))
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 2391b4a485..9b8d740efb 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -185,7 +185,9 @@ func Test_str2nr()
call assert_fails('call str2nr([])', 'E730:')
call assert_fails('call str2nr({->2})', 'E729:')
- call assert_fails('call str2nr(1.2)', 'E806:')
+ if has('float')
+ call assert_fails('call str2nr(1.2)', 'E806:')
+ endif
call assert_fails('call str2nr(10, [])', 'E474:')
endfunc
@@ -325,11 +327,18 @@ func Test_simplify()
call assert_equal('./file', simplify('./dir/../file'))
call assert_equal('../dir/file', simplify('dir/../../dir/file'))
call assert_equal('./file', simplify('dir/.././file'))
+ call assert_equal('../dir', simplify('./../dir'))
+ call assert_equal('..', simplify('../testdir/..'))
+ call mkdir('Xdir')
+ call assert_equal('.', simplify('Xdir/../.'))
+ call delete('Xdir', 'd')
call assert_fails('call simplify({->0})', 'E729:')
call assert_fails('call simplify([])', 'E730:')
call assert_fails('call simplify({})', 'E731:')
- call assert_fails('call simplify(1.2)', 'E806:')
+ if has('float')
+ call assert_fails('call simplify(1.2)', 'E806:')
+ endif
endfunc
func Test_pathshorten()
@@ -1218,6 +1227,16 @@ func Test_input_func()
call assert_fails("call input('F:', '', [])", 'E730:')
endfunc
+" Test for the inputdialog() function
+func Test_inputdialog()
+ CheckNotGui
+
+ call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<CR>", 'xt')
+ call assert_equal('xx', v)
+ call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<Esc>", 'xt')
+ call assert_equal('yy', v)
+endfunc
+
" Test for inputlist()
func Test_inputlist()
call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>1\<cr>", 'tx')
diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim
index 589899f532..1569177d66 100644
--- a/src/nvim/testdir/test_gf.vim
+++ b/src/nvim/testdir/test_gf.vim
@@ -35,6 +35,13 @@ func Test_gf_url()
call search("URL")
call assert_equal("URL://machine.name:1234?q=vim", expand("<cfile>"))
+ %d
+ call setline(1, "demo://remote_file")
+ wincmd f
+ call assert_equal('demo://remote_file', @%)
+ call assert_equal(2, winnr('$'))
+ close!
+
set isf&vim
enew!
endfunc
@@ -63,11 +70,18 @@ func Test_gF()
call assert_equal('Xfile', bufname('%'))
call assert_equal(2, getcurpos()[1])
+ " jumping to the file/line with CTRL-W_F
+ %bw!
+ edit Xfile1
+ call setline(1, ['one', 'Xfile:4', 'three'])
+ exe "normal 2G\<C-W>F"
+ call assert_equal('Xfile', bufname('%'))
+ call assert_equal(4, getcurpos()[1])
+
set isfname&
call delete('Xfile')
call delete('Xfile2')
- bwipe Xfile
- bwipe Xfile2
+ %bw!
endfunc
" Test for invoking 'gf' on a ${VAR} variable
@@ -137,7 +151,7 @@ func Test_gf_visual()
bwipe!
call delete('Xtest_gf_visual')
- set nohidden
+ set hidden&
endfunc
func Test_gf_error()
@@ -147,5 +161,37 @@ func Test_gf_error()
call setline(1, '/doesnotexist')
call assert_fails('normal gf', 'E447:')
call assert_fails('normal gF', 'E447:')
+ call assert_fails('normal [f', 'E447:')
+
+ " gf is not allowed when text is locked
+ au InsertCharPre <buffer> normal! gF<CR>
+ let caught_e565 = 0
+ try
+ call feedkeys("ix\<esc>", 'xt')
+ catch /^Vim\%((\a\+)\)\=:E565/ " catch E565
+ let caught_e565 = 1
+ endtry
+ call assert_equal(1, caught_e565)
+ au! InsertCharPre
+
bwipe!
endfunc
+
+" If a file is not found by 'gf', then 'includeexpr' should be used to locate
+" the file.
+func Test_gf_includeexpr()
+ new
+ let g:Inc_fname = ''
+ func IncFunc()
+ let g:Inc_fname = v:fname
+ return v:fname
+ endfunc
+ setlocal includeexpr=IncFunc()
+ call setline(1, 'somefile.java')
+ call assert_fails('normal gf', 'E447:')
+ call assert_equal('somefile.java', g:Inc_fname)
+ close!
+ delfunc IncFunc
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim
index ad561baf4a..feddf85346 100644
--- a/src/nvim/testdir/test_global.vim
+++ b/src/nvim/testdir/test_global.vim
@@ -66,6 +66,18 @@ func Test_global_print()
close!
endfunc
+" Test for global command with newline character
+func Test_global_newline()
+ new
+ call setline(1, ['foo'])
+ exe "g/foo/s/f/h/\<NL>s/o$/w/"
+ call assert_equal('how', getline(1))
+ call setline(1, ["foo\<NL>bar"])
+ exe "g/foo/s/foo\\\<NL>bar/xyz/"
+ call assert_equal('xyz', getline(1))
+ close!
+endfunc
+
func Test_wrong_delimiter()
call assert_fails('g x^bxd', 'E146:')
endfunc
diff --git a/src/nvim/testdir/test_gn.vim b/src/nvim/testdir/test_gn.vim
index d09b25b0e7..c4a41a6742 100644
--- a/src/nvim/testdir/test_gn.vim
+++ b/src/nvim/testdir/test_gn.vim
@@ -154,8 +154,24 @@ func Test_gn_command()
norm! gg0f2vf7gNd
call assert_equal(['1678'], getline(1,'$'))
sil! %d _
-
set wrapscan&vim
+
+ " Without 'wrapscan', in visual mode, running gn without a match should fail
+ " but the visual mode should be kept.
+ set nowrapscan
+ call setline('.', 'one two')
+ let @/ = 'one'
+ call assert_beeps('normal 0wvlgn')
+ exe "normal y"
+ call assert_equal('tw', @")
+
+ " with exclusive selection, run gn and gN
+ set selection=exclusive
+ normal 0gny
+ call assert_equal('one', @")
+ normal 0wgNy
+ call assert_equal('one', @")
+ set selection&
endfunc
func Test_gN_repeat()
diff --git a/src/nvim/testdir/test_goto.vim b/src/nvim/testdir/test_goto.vim
index 19513b315a..49095400ef 100644
--- a/src/nvim/testdir/test_goto.vim
+++ b/src/nvim/testdir/test_goto.vim
@@ -334,21 +334,24 @@ endfunc
func Test_motion_if_elif_else_endif()
new
- a
-/* Test pressing % on #if, #else #elsif and #endif,
- * with nested #if
- */
-#if FOO
-/* ... */
-# if BAR
-/* ... */
-# endif
-#elif BAR
-/* ... */
-#else
-/* ... */
-#endif
-.
+ let lines =<< trim END
+ /* Test pressing % on #if, #else #elsif and #endif,
+ * with nested #if
+ */
+ #if FOO
+ /* ... */
+ # if BAR
+ /* ... */
+ # endif
+ #elif BAR
+ /* ... */
+ #else
+ /* ... */
+ #endif
+
+ #define FOO 1
+ END
+ call setline(1, lines)
/#if FOO
norm %
call assert_equal([9, 1], getpos('.')[1:2])
@@ -364,6 +367,30 @@ func Test_motion_if_elif_else_endif()
norm $%
call assert_equal([6, 1], getpos('.')[1:2])
+ " Test for [# and ]# command
+ call cursor(5, 1)
+ normal [#
+ call assert_equal([4, 1], getpos('.')[1:2])
+ call cursor(5, 1)
+ normal ]#
+ call assert_equal([9, 1], getpos('.')[1:2])
+ call cursor(10, 1)
+ normal [#
+ call assert_equal([9, 1], getpos('.')[1:2])
+ call cursor(10, 1)
+ normal ]#
+ call assert_equal([11, 1], getpos('.')[1:2])
+
+ " Finding a match before the first line or after the last line should fail
+ normal gg
+ call assert_beeps('normal [#')
+ normal G
+ call assert_beeps('normal ]#')
+
+ " Finding a match for a macro definition (#define) should fail
+ normal G
+ call assert_beeps('normal %')
+
bw!
endfunc
@@ -393,3 +420,5 @@ func Test_motion_c_comment()
bw!
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim
index 9569cfa4e5..dbb36facee 100644
--- a/src/nvim/testdir/test_help.vim
+++ b/src/nvim/testdir/test_help.vim
@@ -92,6 +92,11 @@ func Test_help_local_additions()
let &rtp = rtp_save
endfunc
+func Test_help_completion()
+ call feedkeys(":help :undo\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"help :undo :undoj :undol :undojoin :undolist', @:)
+endfunc
+
" Test for the :helptags command
func Test_helptag_cmd()
call mkdir('Xdir/a/doc', 'p')
diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim
index ece8ccf215..e84726bbfc 100644
--- a/src/nvim/testdir/test_help_tagjump.vim
+++ b/src/nvim/testdir/test_help_tagjump.vim
@@ -125,6 +125,11 @@ func Test_help_tagjump()
call assert_true(getline('.') =~ '\*/\\bar\*')
helpclose
+ help \_$
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*/\\_$\*')
+ helpclose
+
help CTRL-\_CTRL-N
call assert_equal("help", &filetype)
call assert_true(getline('.') =~ '\*CTRL-\\_CTRL-N\*')
diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim
index 2f0dc2dae1..feb521e232 100644
--- a/src/nvim/testdir/test_history.vim
+++ b/src/nvim/testdir/test_history.vim
@@ -1,8 +1,7 @@
" Tests for the history functions
-if !has('cmdline_hist')
- finish
-endif
+source check.vim
+CheckFeature cmdline_hist
set history=7
@@ -71,6 +70,14 @@ function History_Tests(hist)
call assert_equal('', histget(a:hist, i))
call assert_equal('', histget(a:hist, i - 7 - 1))
endfor
+
+ " Test for freeing an entry at the beginning of the history list
+ for i in range(1, 4)
+ call histadd(a:hist, 'text_' . i)
+ endfor
+ call histdel(a:hist, 1)
+ call assert_equal('', histget(a:hist, 1))
+ call assert_equal('text_4', histget(a:hist, 4))
endfunction
function Test_History()
@@ -86,6 +93,8 @@ function Test_History()
call assert_fails('call histget([])', 'E730:')
call assert_equal(-1, histnr('abc'))
call assert_fails('call histnr([])', 'E730:')
+ call assert_fails('history xyz', 'E488:')
+ call assert_fails('history ,abc', 'E488:')
endfunction
function Test_Search_history_window()
@@ -105,7 +114,122 @@ function Test_Search_history_window()
bwipe!
endfunc
+" Test for :history command option completion
function Test_history_completion()
call feedkeys(":history \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"history / : = > ? @ all cmd debug expr input search', @:)
endfunc
+
+" Test for increasing the 'history' option value
+func Test_history_size()
+ let save_histsz = &history
+ set history=10
+ call histadd(':', 'ls')
+ call histdel(':')
+ for i in range(1, 5)
+ call histadd(':', 'cmd' .. i)
+ endfor
+ call assert_equal(5, histnr(':'))
+ call assert_equal('cmd5', histget(':', -1))
+
+ set history=15
+ for i in range(6, 10)
+ call histadd(':', 'cmd' .. i)
+ endfor
+ call assert_equal(10, histnr(':'))
+ call assert_equal('cmd1', histget(':', 1))
+ call assert_equal('cmd10', histget(':', -1))
+
+ set history=5
+ call histadd(':', 'abc')
+ call assert_equal('', histget(':', 6))
+ call assert_equal('', histget(':', 12))
+ call assert_equal('cmd7', histget(':', 7))
+ call assert_equal('abc', histget(':', -1))
+
+ " This test works only when the language is English
+ if v:lang == "C" || v:lang =~ '^[Ee]n'
+ set history=0
+ redir => v
+ call feedkeys(":history\<CR>", 'xt')
+ redir END
+ call assert_equal(["'history' option is zero"], split(v, "\n"))
+ endif
+
+ let &history=save_histsz
+endfunc
+
+" Test for recalling old search patterns in /
+func Test_history_search()
+ call histdel('/')
+ let g:pat = []
+ func SavePat()
+ call add(g:pat, getcmdline())
+ return ''
+ endfunc
+ cnoremap <F2> <C-\>eSavePat()<CR>
+ call histadd('/', 'pat1')
+ call histadd('/', 'pat2')
+ let @/ = ''
+ call feedkeys("/\<Up>\<F2>\<Up>\<F2>\<Down>\<Down>\<F2>\<Esc>", 'xt')
+ call assert_equal(['pat2', 'pat1', ''], g:pat)
+ cunmap <F2>
+ delfunc SavePat
+
+ " Search for a pattern that is not present in the history
+ call assert_beeps('call feedkeys("/a1b2\<Up>\<CR>", "xt")')
+
+ " Recall patterns with 'history' set to 0
+ set history=0
+ let @/ = 'abc'
+ let cmd = 'call feedkeys("/\<Up>\<Down>\<S-Up>\<S-Down>\<CR>", "xt")'
+ call assert_fails(cmd, 'E486:')
+ set history&
+
+ " Recall patterns till the end of history
+ set history=4
+ call histadd('/', 'pat')
+ call histdel('/')
+ call histadd('/', 'pat1')
+ call histadd('/', 'pat2')
+ call assert_beeps('call feedkeys("/\<Up>\<Up>\<Up>\<C-U>\<cr>", "xt")')
+ call assert_beeps('call feedkeys("/\<Down><cr>", "xt")')
+
+ " Test for wrapping around the history list
+ for i in range(3, 7)
+ call histadd('/', 'pat' .. i)
+ endfor
+ let upcmd = "\<up>\<up>\<up>\<up>\<up>"
+ let downcmd = "\<down>\<down>\<down>\<down>\<down>"
+ try
+ call feedkeys("/" .. upcmd .. "\<cr>", 'xt')
+ catch /E486:/
+ endtry
+ call assert_equal('pat4', @/)
+ try
+ call feedkeys("/" .. upcmd .. downcmd .. "\<cr>", 'xt')
+ catch /E486:/
+ endtry
+ call assert_equal('pat4', @/)
+
+ " Test for changing the search command separator in the history
+ call assert_fails('call feedkeys("/def/\<cr>", "xt")', 'E486:')
+ call assert_fails('call feedkeys("?\<up>\<cr>", "xt")', 'E486:')
+ call assert_equal('def?', histget('/', -1))
+
+ call assert_fails('call feedkeys("/ghi?\<cr>", "xt")', 'E486:')
+ call assert_fails('call feedkeys("?\<up>\<cr>", "xt")', 'E486:')
+ call assert_equal('ghi\?', histget('/', -1))
+
+ set history&
+endfunc
+
+" Test for making sure the key value is not stored in history
+func Test_history_crypt_key()
+ CheckFeature cryptv
+ call feedkeys(":set bs=2 key=abc ts=8\<CR>", 'xt')
+ call assert_equal('set bs=2 key= ts=8', histget(':'))
+ set key& bs& ts&
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim
index 12fe52f057..2559654f25 100644
--- a/src/nvim/testdir/test_increment.vim
+++ b/src/nvim/testdir/test_increment.vim
@@ -776,6 +776,14 @@ func Test_increment_empty_line()
call setline(1, ['0', '0', '0', '0', '0', '0', ''])
exe "normal Gvgg\<C-A>"
call assert_equal(['1', '1', '1', '1', '1', '1', ''], getline(1, 7))
+
+ " Ctrl-A/Ctrl-X should do nothing in operator pending mode
+ %d
+ call setline(1, 'one two')
+ exe "normal! c\<C-A>l"
+ exe "normal! c\<C-X>l"
+ call assert_equal('one two', getline(1))
+
bwipe!
endfunc
diff --git a/src/nvim/testdir/test_indent.vim b/src/nvim/testdir/test_indent.vim
index 516b3fbdd1..3b5b643177 100644
--- a/src/nvim/testdir/test_indent.vim
+++ b/src/nvim/testdir/test_indent.vim
@@ -121,4 +121,159 @@ func Test_copyindent()
close!
endfunc
+" Test for changing multiple lines with lisp indent
+func Test_lisp_indent_change_multiline()
+ new
+ setlocal lisp autoindent
+ call setline(1, ['(if a', ' (if b', ' (return 5)))'])
+ normal! jc2j(return 4))
+ call assert_equal(' (return 4))', getline(2))
+ close!
+endfunc
+
+func Test_lisp_indent()
+ new
+ setlocal lisp autoindent
+ call setline(1, ['(if a', ' ;; comment', ' \ abc', '', ' " str1\', ' " st\b', ' (return 5)'])
+ normal! jo;; comment
+ normal! jo\ abc
+ normal! jo;; ret
+ normal! jostr1"
+ normal! jostr2"
+ call assert_equal([' ;; comment', ' ;; comment', ' \ abc', ' \ abc', '', ' ;; ret', ' " str1\', ' str1"', ' " st\b', ' str2"'], getline(2, 11))
+ close!
+endfunc
+
+func Test_lisp_indent_quoted()
+ " This was going past the end of the line
+ new
+ setlocal lisp autoindent
+ call setline(1, ['"[', '='])
+ normal Gvk=
+
+ bwipe!
+endfunc
+
+" Test for setting the 'indentexpr' from a modeline
+func Test_modeline_indent_expr()
+ let modeline = &modeline
+ set modeline
+ func GetIndent()
+ return line('.') * 2
+ endfunc
+ call writefile(['# vim: indentexpr=GetIndent()'], 'Xfile.txt')
+ set modelineexpr
+ new Xfile.txt
+ call assert_equal('GetIndent()', &indentexpr)
+ exe "normal Oa\nb\n"
+ call assert_equal([' a', ' b'], getline(1, 2))
+
+ set modelineexpr&
+ delfunc GetIndent
+ let &modeline = modeline
+ close!
+ call delete('Xfile.txt')
+endfunc
+
+func Test_indent_func_with_gq()
+
+ function GetTeXIndent()
+ " Sample indent expression for TeX files
+ let lnum = prevnonblank(v:lnum - 1)
+ " At the start of the file use zero indent.
+ if lnum == 0
+ return 0
+ endif
+ let line = getline(lnum)
+ let ind = indent(lnum)
+ " Add a 'shiftwidth' after beginning of environments.
+ if line =~ '\\begin{center}'
+ let ind = ind + shiftwidth()
+ endif
+ return ind
+ endfunction
+
+ new
+ setl et sw=2 sts=2 ts=2 tw=50 indentexpr=GetTeXIndent()
+ put =[ '\documentclass{article}', '', '\begin{document}', '',
+ \ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce ut enim non',
+ \ 'libero efficitur aliquet. Maecenas metus justo, facilisis convallis blandit',
+ \ 'non, semper eu urna. Suspendisse diam diam, iaculis faucibus lorem eu,',
+ \ 'fringilla condimentum lectus. Quisque euismod diam at convallis vulputate.',
+ \ 'Pellentesque laoreet tortor sit amet mauris euismod ornare. Sed varius',
+ \ 'bibendum orci vel vehicula. Pellentesque tempor, ipsum et auctor accumsan,',
+ \ 'metus lectus ultrices odio, sed elementum mi ante at arcu.', '', '\begin{center}', '',
+ \ 'Proin nec risus consequat nunc dapibus consectetur. Mauris lacinia est a augue',
+ \ 'tristique accumsan. Morbi pretium, felis molestie eleifend condimentum, arcu',
+ \ 'ipsum congue nisl, quis euismod purus libero in ante.', '',
+ \ 'Donec id semper purus.',
+ \ 'Suspendisse eget aliquam nunc. Maecenas fringilla mauris vitae maximus',
+ \ 'condimentum. Cras a quam in mi dictum eleifend at a lorem. Sed convallis',
+ \ 'ante a commodo facilisis. Nam suscipit vulputate odio, vel dapibus nisl',
+ \ 'dignissim facilisis. Vestibulum ante ipsum primis in faucibus orci luctus et',
+ \ 'ultrices posuere cubilia curae;', '', '']
+ 1d_
+ call cursor(5, 1)
+ ka
+ call cursor(14, 1)
+ kb
+ norm! 'agqap
+ norm! 'bgqG
+ let expected = [ '\documentclass{article}', '', '\begin{document}', '',
+ \ 'Lorem ipsum dolor sit amet, consectetur adipiscing',
+ \ 'elit. Fusce ut enim non libero efficitur aliquet.',
+ \ 'Maecenas metus justo, facilisis convallis blandit',
+ \ 'non, semper eu urna. Suspendisse diam diam,',
+ \ 'iaculis faucibus lorem eu, fringilla condimentum',
+ \ 'lectus. Quisque euismod diam at convallis',
+ \ 'vulputate. Pellentesque laoreet tortor sit amet',
+ \ 'mauris euismod ornare. Sed varius bibendum orci',
+ \ 'vel vehicula. Pellentesque tempor, ipsum et auctor',
+ \ 'accumsan, metus lectus ultrices odio, sed',
+ \ 'elementum mi ante at arcu.', '', '\begin{center}', '',
+ \ ' Proin nec risus consequat nunc dapibus',
+ \ ' consectetur. Mauris lacinia est a augue',
+ \ ' tristique accumsan. Morbi pretium, felis',
+ \ ' molestie eleifend condimentum, arcu ipsum congue',
+ \ ' nisl, quis euismod purus libero in ante.',
+ \ '',
+ \ ' Donec id semper purus. Suspendisse eget aliquam',
+ \ ' nunc. Maecenas fringilla mauris vitae maximus',
+ \ ' condimentum. Cras a quam in mi dictum eleifend',
+ \ ' at a lorem. Sed convallis ante a commodo',
+ \ ' facilisis. Nam suscipit vulputate odio, vel',
+ \ ' dapibus nisl dignissim facilisis. Vestibulum',
+ \ ' ante ipsum primis in faucibus orci luctus et',
+ \ ' ultrices posuere cubilia curae;', '', '']
+ call assert_equal(expected, getline(1, '$'))
+
+ bwipe!
+ delmark ab
+ delfunction GetTeXIndent
+endfu
+
+func Test_formatting_keeps_first_line_indent()
+ let lines =<< trim END
+ foo()
+ {
+ int x; // manually positioned
+ // more text that will be formatted
+ // but not reindented
+ END
+ new
+ call setline(1, lines)
+ setlocal sw=4 cindent tw=45 et
+ normal! 4Ggqj
+ let expected =<< trim END
+ foo()
+ {
+ int x; // manually positioned
+ // more text that will be
+ // formatted but not
+ // reindented
+ END
+ call assert_equal(expected, getline(1, '$'))
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index c8104c6b73..93ab17955d 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -95,7 +95,7 @@ func Test_ins_complete()
call delete('Xtest11.one')
call delete('Xtest11.two')
call delete('Xtestdata')
- set cpt& cot& def& tags& tagbsearch& nohidden
+ set cpt& cot& def& tags& tagbsearch& hidden&
cd ..
call delete('Xdir', 'rf')
endfunc
@@ -601,6 +601,107 @@ func Test_ins_compl_tag_sft()
%bwipe!
endfunc
+" Test for 'completefunc' deleting text
+func Test_completefunc_error()
+ new
+ " delete text when called for the first time
+ func CompleteFunc(findstart, base)
+ if a:findstart == 1
+ normal dd
+ return col('.') - 1
+ endif
+ return ['a', 'b']
+ endfunc
+ set completefunc=CompleteFunc
+ call setline(1, ['', 'abcd', ''])
+ call assert_fails('exe "normal 2G$a\<C-X>\<C-U>"', 'E565:')
+
+ " delete text when called for the second time
+ func CompleteFunc2(findstart, base)
+ if a:findstart == 1
+ return col('.') - 1
+ endif
+ normal dd
+ return ['a', 'b']
+ endfunc
+ set completefunc=CompleteFunc2
+ call setline(1, ['', 'abcd', ''])
+ call assert_fails('exe "normal 2G$a\<C-X>\<C-U>"', 'E565:')
+
+ " Jump to a different window from the complete function
+ func CompleteFunc3(findstart, base)
+ if a:findstart == 1
+ return col('.') - 1
+ endif
+ wincmd p
+ return ['a', 'b']
+ endfunc
+ set completefunc=CompleteFunc3
+ new
+ call assert_fails('exe "normal a\<C-X>\<C-U>"', 'E565:')
+ close!
+
+ set completefunc&
+ delfunc CompleteFunc
+ delfunc CompleteFunc2
+ delfunc CompleteFunc3
+ close!
+endfunc
+
+" Test for returning non-string values from 'completefunc'
+func Test_completefunc_invalid_data()
+ new
+ func! CompleteFunc(findstart, base)
+ if a:findstart == 1
+ return col('.') - 1
+ endif
+ return [{}, '', 'moon']
+ endfunc
+ set completefunc=CompleteFunc
+ exe "normal i\<C-X>\<C-U>"
+ call assert_equal('moon', getline(1))
+ set completefunc&
+ close!
+endfunc
+
+" Test for errors in using complete() function
+func Test_complete_func_error()
+ call assert_fails('call complete(1, ["a"])', 'E785:')
+ func ListColors()
+ call complete(col('.'), "blue")
+ endfunc
+ call assert_fails('exe "normal i\<C-R>=ListColors()\<CR>"', 'E474:')
+ func ListMonths()
+ call complete(col('.'), test_null_list())
+ endfunc
+ " Nvim allows a NULL list
+ " call assert_fails('exe "normal i\<C-R>=ListMonths()\<CR>"', 'E474:')
+ delfunc ListColors
+ delfunc ListMonths
+ call assert_fails('call complete_info({})', 'E714:')
+ call assert_equal([], complete_info(['items']).items)
+endfunc
+
+" Test for completing words following a completed word in a line
+func Test_complete_wrapscan()
+ " complete words from another buffer
+ new
+ call setline(1, ['one two', 'three four'])
+ new
+ setlocal complete=w
+ call feedkeys("itw\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>", 'xt')
+ call assert_equal('two three four', getline(1))
+ close!
+ " complete words from the current buffer
+ setlocal complete=.
+ %d
+ call setline(1, ['one two', ''])
+ call cursor(2, 1)
+ call feedkeys("ion\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>", 'xt')
+ call assert_equal('one two one two', getline(2))
+ close!
+endfunc
+
" Test for completing special characters
func Test_complete_special_chars()
new
diff --git a/src/nvim/testdir/test_join.vim b/src/nvim/testdir/test_join.vim
index ecb969d10a..1f7a0825a5 100644
--- a/src/nvim/testdir/test_join.vim
+++ b/src/nvim/testdir/test_join.vim
@@ -51,7 +51,7 @@ func Test_join_marks()
/^This line/;'}-join
call assert_equal([0, 4, 11, 0], getpos("'["))
- call assert_equal([0, 4, 66, 0], getpos("']"))
+ call assert_equal([0, 4, 67, 0], getpos("']"))
enew!
endfunc
@@ -437,5 +437,11 @@ func Test_join_lines()
call setline(1, ['a', 'b', '', 'c', 'd'])
normal 5J
call assert_equal('a b c d', getline(1))
+ call setline(1, ['a', 'b', 'c'])
+ 2,2join
+ call assert_equal(['a', 'b', 'c'], getline(1, '$'))
+ call assert_equal(2, line('.'))
+ 2join
+ call assert_equal(['a', 'b c'], getline(1, '$'))
bwipe!
endfunc
diff --git a/src/nvim/testdir/test_langmap.vim b/src/nvim/testdir/test_langmap.vim
index eca8a95564..4f831aa40b 100644
--- a/src/nvim/testdir/test_langmap.vim
+++ b/src/nvim/testdir/test_langmap.vim
@@ -1,5 +1,8 @@
" tests for 'langmap'
+source check.vim
+CheckFeature langmap
+
func Test_langmap()
new
set langmap=}l,^x,%v
diff --git a/src/nvim/testdir/test_legacy_filetype.vim b/src/nvim/testdir/test_legacy_filetype.vim
new file mode 100644
index 0000000000..772faaadb0
--- /dev/null
+++ b/src/nvim/testdir/test_legacy_filetype.vim
@@ -0,0 +1,4 @@
+let g:do_legacy_filetype = 1
+filetype on
+
+source test_filetype.vim
diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim
index 2fda12d8b4..affa0f96fa 100644
--- a/src/nvim/testdir/test_listlbr.vim
+++ b/src/nvim/testdir/test_listlbr.vim
@@ -2,9 +2,9 @@
scriptencoding latin1
-if !exists("+linebreak") || !has("conceal")
- finish
-endif
+source check.vim
+CheckOption linebreak
+CheckFeature conceal
source view_util.vim
diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim
index 1f100d6244..df1ed78119 100644
--- a/src/nvim/testdir/test_listlbr_utf8.vim
+++ b/src/nvim/testdir/test_listlbr_utf8.vim
@@ -3,9 +3,10 @@
set encoding=utf-8
scriptencoding utf-8
-if !exists("+linebreak") || !has("conceal") || !has("signs")
- finish
-endif
+source check.vim
+CheckOption linebreak
+CheckFeature conceal
+CheckFeature signs
source view_util.vim
diff --git a/src/nvim/testdir/test_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim
index 2b346e0720..c53c07d991 100644
--- a/src/nvim/testdir/test_makeencoding.vim
+++ b/src/nvim/testdir/test_makeencoding.vim
@@ -4,8 +4,7 @@ source shared.vim
let s:python = PythonProg()
if s:python == ''
- " Can't run this test.
- finish
+ throw 'Skipped: python program missing'
endif
let s:script = 'test_makeencoding.py'
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index 494f09e0e5..e1d0b9a9ba 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -31,6 +31,7 @@ func Test_abclear()
abclear
call assert_equal("\n\nNo abbreviation found", execute('abbrev'))
+ call assert_fails('%abclear', 'E481:')
endfunc
func Test_abclear_buffer()
@@ -642,6 +643,13 @@ func Test_map_error()
map <expr> ,f abc
call assert_fails('normal ,f', 'E121:')
unmap <expr> ,f
+
+ " Recursive use of :normal in a map
+ set maxmapdepth=100
+ map gq :normal gq<CR>
+ call assert_fails('normal gq', 'E192:')
+ unmap gq
+ set maxmapdepth&
endfunc
" Test for <special> key mapping
@@ -948,6 +956,16 @@ func Test_map_cmdkey_redo()
ounmap i-
endfunc
+" Test for using <script> with a map to remap characters in rhs
+func Test_script_local_remap()
+ new
+ inoremap <buffer> <SID>xyz mno
+ inoremap <buffer> <script> abc st<SID>xyzre
+ normal iabc
+ call assert_equal('stmnore', getline(1))
+ bwipe!
+endfunc
+
func Test_abbreviate_multi_byte()
new
iabbrev foo bar
diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim
index 00ee8f6d6a..74e63d9d69 100644
--- a/src/nvim/testdir/test_marks.vim
+++ b/src/nvim/testdir/test_marks.vim
@@ -215,6 +215,10 @@ func Test_mark_error()
call assert_fails('mark', 'E471:')
call assert_fails('mark xx', 'E488:')
call assert_fails('mark _', 'E191:')
+ call assert_beeps('normal! m~')
+
+ call setpos("'k", [0, 100, 1, 0])
+ call assert_fails("normal 'k", 'E19:')
endfunc
" Test for :lockmarks when pasting content
@@ -232,6 +236,38 @@ func Test_lockmarks_with_put()
bwipe!
endfunc
+" Test for :k command to set a mark
+func Test_marks_k_cmd()
+ new
+ call setline(1, ['foo', 'bar', 'baz', 'qux'])
+ 1,3kr
+ call assert_equal([0, 3, 1, 0], getpos("'r"))
+ close!
+endfunc
+
+" Test for file marks (A-Z)
+func Test_file_mark()
+ new Xone
+ call setline(1, ['aaa', 'bbb'])
+ norm! G$mB
+ w!
+ new Xtwo
+ call setline(1, ['ccc', 'ddd'])
+ norm! GmD
+ w!
+
+ enew
+ normal! `B
+ call assert_equal('Xone', bufname())
+ call assert_equal([2, 3], [line('.'), col('.')])
+ normal! 'D
+ call assert_equal('Xtwo', bufname())
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ call delete('Xone')
+ call delete('Xtwo')
+endfunc
+
" Test for the getmarklist() function
func Test_getmarklist()
new
diff --git a/src/nvim/testdir/test_matchadd_conceal_utf8.vim b/src/nvim/testdir/test_matchadd_conceal_utf8.vim
index 1d0c740734..f33c7f694c 100644
--- a/src/nvim/testdir/test_matchadd_conceal_utf8.vim
+++ b/src/nvim/testdir/test_matchadd_conceal_utf8.vim
@@ -1,7 +1,7 @@
" Test for matchadd() and conceal feature using utf-8.
-if !has('conceal')
- finish
-endif
+
+source check.vim
+CheckFeature conceal
func s:screenline(lnum) abort
let line = []
diff --git a/src/nvim/testdir/test_matchfuzzy.vim b/src/nvim/testdir/test_matchfuzzy.vim
index 6b4949ad8c..c836bc87aa 100644
--- a/src/nvim/testdir/test_matchfuzzy.vim
+++ b/src/nvim/testdir/test_matchfuzzy.vim
@@ -59,7 +59,7 @@ func Test_matchfuzzy()
%bw!
eval ['somebuf', 'anotherone', 'needle', 'yetanotherone']->map({_, v -> bufadd(v) + bufload(v)})
- let l = getbufinfo()->map({_, v -> v.name})->matchfuzzy('ndl')
+ let l = getbufinfo()->map({_, v -> fnamemodify(v.name, ':t')})->matchfuzzy('ndl')
call assert_equal(1, len(l))
call assert_match('needle', l[0])
diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim
index de6d4aa359..4af75be514 100644
--- a/src/nvim/testdir/test_menu.vim
+++ b/src/nvim/testdir/test_menu.vim
@@ -1,8 +1,7 @@
" Test that the system menu can be loaded.
-if !has('menu')
- finish
-endif
+source check.vim
+CheckFeature menu
func Test_load_menu()
try
@@ -36,3 +35,92 @@ func Test_translate_menu()
source $VIMRUNTIME/delmenu.vim
endfunc
+
+func Test_menu_commands()
+ nmenu 2 Test.FooBar :let g:did_menu = 'normal'<CR>
+ vmenu 2 Test.FooBar :let g:did_menu = 'visual'<CR>
+ smenu 2 Test.FooBar :let g:did_menu = 'select'<CR>
+ omenu 2 Test.FooBar :let g:did_menu = 'op-pending'<CR>
+ tlmenu 2 Test.FooBar :let g:did_menu = 'terminal'<CR>
+ imenu 2 Test.FooBar :let g:did_menu = 'insert'<CR>
+ cmenu 2 Test.FooBar :let g:did_menu = 'cmdline'<CR>
+ emenu n Test.FooBar
+
+ call feedkeys(":menu Test.FooB\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"menu Test.FooBar', @:)
+
+ call assert_equal('normal', g:did_menu)
+ emenu v Test.FooBar
+ call assert_equal('visual', g:did_menu)
+ emenu s Test.FooBar
+ call assert_equal('select', g:did_menu)
+ emenu o Test.FooBar
+ call assert_equal('op-pending', g:did_menu)
+ emenu t Test.FooBar
+ call assert_equal('terminal', g:did_menu)
+ emenu i Test.FooBar
+ call assert_equal('insert', g:did_menu)
+ emenu c Test.FooBar
+ call assert_equal('cmdline', g:did_menu)
+
+ nunmenu Test.FooBar
+ call assert_fails('emenu n Test.FooBar', 'E335: Menu not defined for Normal mode')
+ vunmenu Test.FooBar
+ call assert_fails('emenu v Test.FooBar', 'E335: Menu not defined for Visual mode')
+ vmenu 2 Test.FooBar :let g:did_menu = 'visual'<CR>
+ sunmenu Test.FooBar
+ call assert_fails('emenu s Test.FooBar', 'E335: Menu not defined for Select mode')
+ ounmenu Test.FooBar
+ call assert_fails('emenu o Test.FooBar', 'E335: Menu not defined for Op-pending mode')
+ iunmenu Test.FooBar
+ call assert_fails('emenu i Test.FooBar', 'E335: Menu not defined for Insert mode')
+ cunmenu Test.FooBar
+ call assert_fails('emenu c Test.FooBar', 'E335: Menu not defined for Cmdline mode')
+ tlunmenu Test.FooBar
+ call assert_fails('emenu t Test.FooBar', 'E335: Menu not defined for Terminal mode')
+
+ aunmenu Test.FooBar
+ call assert_fails('emenu n Test.FooBar', 'E334:')
+
+ nmenu 2 Test.FooBar.Child :let g:did_menu = 'foobar'<CR>
+ call assert_fails('emenu n Test.FooBar', 'E333:')
+ nunmenu Test.FooBar.Child
+
+ unlet g:did_menu
+endfun
+
+" Test for menu item completion in command line
+func Test_menu_expand()
+ " Create the menu itmes for test
+ for i in range(1, 4)
+ let m = 'menu Xmenu.A' .. i .. '.A' .. i
+ for j in range(1, 4)
+ exe m .. 'B' .. j .. ' :echo "A' .. i .. 'B' .. j .. '"' .. "<CR>"
+ endfor
+ endfor
+ set wildmenu
+
+ " Test for <CR> selecting a submenu
+ call feedkeys(":emenu Xmenu.A\<Tab>\<CR>\<Right>x\<BS>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"emenu Xmenu.A1.A1B2', @:)
+
+ " Test for <Down> selecting a submenu
+ call feedkeys(":emenu Xmenu.A\<Tab>\<Right>\<Right>\<Down>" ..
+ \ "\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"emenu Xmenu.A3.A3B1 A3B2 A3B3 A3B4', @:)
+
+ " Test for <Up> to go up a submenu
+ call feedkeys(":emenu Xmenu.A\<Tab>\<Down>\<Up>\<Right>\<Right>" ..
+ \ "\<Left>\<Down>\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"emenu Xmenu.A2.A2B1 A2B2 A2B3 A2B4', @:)
+
+ " Test for <Up> to go up a menu
+ call feedkeys(":emenu Xmenu.A\<Tab>\<Down>\<Up>\<Up>\<Up>" ..
+ \ "\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"emenu Buffers. Xmenu.', @:)
+
+ set wildmenu&
+ unmenu Xmenu
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index 8be0c79499..5670368936 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -112,6 +112,136 @@ func Test_echospace()
set ruler& showcmd&
endfunc
+" Test more-prompt (see :help more-prompt).
+func Test_message_more()
+ CheckRunVimInTerminal
+ let buf = RunVimInTerminal('', {'rows': 6})
+ call term_sendkeys(buf, ":call setline(1, range(1, 100))\n")
+
+ call term_sendkeys(buf, ":%p#\n")
+ call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+
+ call term_sendkeys(buf, '?')
+ call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More -- SPACE/d/j: screen/page/line down, b/u/k: up, q: quit ', term_getline(buf, 6))})
+
+ " Down a line with j, <CR>, <NL> or <Down>.
+ call term_sendkeys(buf, "j")
+ call WaitForAssert({-> assert_equal(' 6 6', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+ call term_sendkeys(buf, "\<NL>")
+ call WaitForAssert({-> assert_equal(' 7 7', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<CR>")
+ call WaitForAssert({-> assert_equal(' 8 8', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<Down>")
+ call WaitForAssert({-> assert_equal(' 9 9', term_getline(buf, 5))})
+
+ " Down a screen with <Space>, f, or <PageDown>.
+ call term_sendkeys(buf, 'f')
+ call WaitForAssert({-> assert_equal(' 14 14', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+ call term_sendkeys(buf, ' ')
+ call WaitForAssert({-> assert_equal(' 19 19', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<PageDown>")
+ call WaitForAssert({-> assert_equal(' 24 24', term_getline(buf, 5))})
+
+ " Down a page (half a screen) with d.
+ call term_sendkeys(buf, 'd')
+ call WaitForAssert({-> assert_equal(' 27 27', term_getline(buf, 5))})
+
+ " Down all the way with 'G'.
+ call term_sendkeys(buf, 'G')
+ call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))})
+
+ " Up a line k, <BS> or <Up>.
+ call term_sendkeys(buf, 'k')
+ call WaitForAssert({-> assert_equal(' 99 99', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<BS>")
+ call WaitForAssert({-> assert_equal(' 98 98', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<Up>")
+ call WaitForAssert({-> assert_equal(' 97 97', term_getline(buf, 5))})
+
+ " Up a screen with b or <PageUp>.
+ call term_sendkeys(buf, 'b')
+ call WaitForAssert({-> assert_equal(' 92 92', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<PageUp>")
+ call WaitForAssert({-> assert_equal(' 87 87', term_getline(buf, 5))})
+
+ " Up a page (half a screen) with u.
+ call term_sendkeys(buf, 'u')
+ call WaitForAssert({-> assert_equal(' 84 84', term_getline(buf, 5))})
+
+ " Up all the way with 'g'.
+ call term_sendkeys(buf, 'g')
+ call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+
+ " All the way down. Pressing f should do nothing but pressing
+ " space should end the more prompt.
+ call term_sendkeys(buf, 'G')
+ call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))})
+ call term_sendkeys(buf, 'f')
+ call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 5))})
+ call term_sendkeys(buf, ' ')
+ call WaitForAssert({-> assert_equal('100', term_getline(buf, 5))})
+
+ " Pressing g< shows the previous command output.
+ call term_sendkeys(buf, 'g<')
+ call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))})
+
+ call term_sendkeys(buf, ":%p#\n")
+ call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+
+ " Stop command output with q, <Esc> or CTRL-C.
+ call term_sendkeys(buf, 'q')
+ call WaitForAssert({-> assert_equal('100', term_getline(buf, 5))})
+
+ " Execute a : command from the more prompt
+ call term_sendkeys(buf, ":%p#\n")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+ call term_sendkeys(buf, ":")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal(':', term_getline(buf, 6))})
+ call term_sendkeys(buf, "echo 'Hello'\n")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal('Hello ', term_getline(buf, 5))})
+
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_ask_yesno()
+ CheckRunVimInTerminal
+ let buf = RunVimInTerminal('', {'rows': 6})
+ call term_sendkeys(buf, ":call setline(1, range(1, 2))\n")
+
+ call term_sendkeys(buf, ":2,1s/^/n/\n")
+ call WaitForAssert({-> assert_equal('Backwards range given, OK to swap (y/n)?', term_getline(buf, 6))})
+ call term_sendkeys(buf, "n")
+ call WaitForAssert({-> assert_match('^Backwards range given, OK to swap (y/n)?n *1,1 *All$', term_getline(buf, 6))})
+ call WaitForAssert({-> assert_equal('1', term_getline(buf, 1))})
+
+ call term_sendkeys(buf, ":2,1s/^/Esc/\n")
+ call WaitForAssert({-> assert_equal('Backwards range given, OK to swap (y/n)?', term_getline(buf, 6))})
+ call term_sendkeys(buf, "\<Esc>")
+ call WaitForAssert({-> assert_match('^Backwards range given, OK to swap (y/n)?n *1,1 *All$', term_getline(buf, 6))})
+ call WaitForAssert({-> assert_equal('1', term_getline(buf, 1))})
+
+ call term_sendkeys(buf, ":2,1s/^/y/\n")
+ call WaitForAssert({-> assert_equal('Backwards range given, OK to swap (y/n)?', term_getline(buf, 6))})
+ call term_sendkeys(buf, "y")
+ call WaitForAssert({-> assert_match('^Backwards range given, OK to swap (y/n)?y *2,1 *All$', term_getline(buf, 6))})
+ call WaitForAssert({-> assert_equal('y1', term_getline(buf, 1))})
+ call WaitForAssert({-> assert_equal('y2', term_getline(buf, 2))})
+
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_mapping_at_hit_return_prompt()
nnoremap <C-B> :echo "hit ctrl-b"<CR>
call feedkeys(":ls\<CR>", "xt")
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index cf90e416c4..8ec408e62e 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -2,9 +2,8 @@
scriptencoding latin1
-if !has('mksession')
- finish
-endif
+source check.vim
+CheckFeature mksession
source shared.vim
source term_util.vim
@@ -43,9 +42,9 @@ func Test_mksession()
\ ' four leadinG spaces',
\ 'two consecutive tabs',
\ 'two tabs in one line',
- \ 'one ä multibyteCharacter',
- \ 'aä Ä two multiByte characters',
- \ 'Aäöü three mulTibyte characters',
+ \ 'one ä multibyteCharacter',
+ \ 'aä Ä two multiByte characters',
+ \ 'Aäöü three mulTibyte characters',
\ 'short line',
\ ])
let tmpfile = 'Xtemp'
diff --git a/src/nvim/testdir/test_mksession_utf8.vim b/src/nvim/testdir/test_mksession_utf8.vim
index 722fd28beb..4e593cc21a 100644
--- a/src/nvim/testdir/test_mksession_utf8.vim
+++ b/src/nvim/testdir/test_mksession_utf8.vim
@@ -3,9 +3,8 @@
set encoding=utf-8
scriptencoding utf-8
-if !has('mksession')
- finish
-endif
+source check.vim
+CheckFeature mksession
func Test_mksession_utf8()
tabnew
diff --git a/src/nvim/testdir/test_move.vim b/src/nvim/testdir/test_move.vim
index f666a904b0..8c40369dbd 100644
--- a/src/nvim/testdir/test_move.vim
+++ b/src/nvim/testdir/test_move.vim
@@ -38,6 +38,7 @@ func Test_move()
call assert_fails("move -100", 'E16:')
call assert_fails("move +100", 'E16:')
call assert_fails('move', 'E16:')
+ call assert_fails("move 'r", 'E20:')
%bwipeout!
endfunc
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index d1cb89bbd4..9fbd1f774a 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -1,6 +1,8 @@
" Test for various Normal mode commands
source shared.vim
+source check.vim
+source view_util.vim
func Setup_NewWindow()
10new
@@ -53,7 +55,7 @@ func OpfuncDummy(type, ...)
let g:bufnr=bufnr('%')
endfunc
-fun! Test_normal00_optrans()
+func Test_normal00_optrans()
new
call append(0, ['1 This is a simple test: abcd', '2 This is the second line', '3 this is the third line'])
1
@@ -95,6 +97,12 @@ func Test_normal01_keymodel()
50
call feedkeys("\<S-Up>y", 'tx')
call assert_equal(['49', '5'], getreg(0, 0, 1))
+ " Use the different Shift special keys
+ 50
+ call feedkeys("\<S-Right>\<S-Left>\<S-Up>\<S-Down>\<S-Home>\<S-End>y", 'tx')
+ call assert_equal(['50'], getline("'<", "'>"))
+ call assert_equal(['50', ''], getreg(0, 0, 1))
+
" Do not start visual mode when keymodel=
set keymodel=
50
@@ -115,8 +123,8 @@ func Test_normal01_keymodel()
bw!
endfunc
+" Test for select mode
func Test_normal02_selectmode()
- " some basic select mode tests
call Setup_NewWindow()
50
norm! gHy
@@ -345,7 +353,7 @@ func Test_normal08_fold()
bw!
endfunc
-func Test_normal09_operatorfunc()
+func Test_normal09a_operatorfunc()
" Test operatorfunc
call Setup_NewWindow()
" Add some spaces for counting
@@ -375,7 +383,7 @@ func Test_normal09_operatorfunc()
bw!
endfunc
-func Test_normal09a_operatorfunc()
+func Test_normal09b_operatorfunc()
" Test operatorfunc
call Setup_NewWindow()
" Add some spaces for counting
@@ -397,10 +405,45 @@ func Test_normal09a_operatorfunc()
" clean up
unmap <buffer> ,,
set opfunc=
+ call assert_fails('normal Vg@', 'E774:')
bw!
unlet! g:opt
endfunc
+func OperatorfuncRedo(_)
+ let g:opfunc_count = v:count
+endfunc
+
+func Underscorize(_)
+ normal! '[V']r_
+endfunc
+
+func Test_normal09c_operatorfunc()
+ " Test redoing operatorfunc
+ new
+ call setline(1, 'some text')
+ set operatorfunc=OperatorfuncRedo
+ normal v3g@
+ call assert_equal(3, g:opfunc_count)
+ let g:opfunc_count = 0
+ normal .
+ call assert_equal(3, g:opfunc_count)
+
+ bw!
+ unlet g:opfunc_count
+
+ " Test redoing Visual mode
+ set operatorfunc=Underscorize
+ new
+ call setline(1, ['first', 'first', 'third', 'third', 'second'])
+ normal! 1GVjg@
+ normal! 5G.
+ normal! 3G.
+ call assert_equal(['_____', '_____', '_____', '_____', '______'], getline(1, '$'))
+ bwipe!
+ set operatorfunc=
+endfunc
+
func Test_normal10_expand()
" Test for expand()
10new
@@ -451,13 +494,33 @@ func Test_normal11_showcmd()
bw!
endfunc
+" Test for nv_error and normal command errors
func Test_normal12_nv_error()
- " Test for nv_error
10new
call setline(1, range(1,5))
" should not do anything, just beep
- exe "norm! <c-k>"
+ call assert_beeps('exe "norm! <c-k>"')
call assert_equal(map(range(1,5), 'string(v:val)'), getline(1,'$'))
+ call assert_beeps('normal! G2dd')
+ call assert_beeps("normal! g\<C-A>")
+ call assert_beeps("normal! g\<C-X>")
+ call assert_beeps("normal! g\<C-B>")
+ " call assert_beeps("normal! vQ\<Esc>")
+ call assert_beeps("normal! 2[[")
+ call assert_beeps("normal! 2]]")
+ call assert_beeps("normal! 2[]")
+ call assert_beeps("normal! 2][")
+ call assert_beeps("normal! 4[z")
+ call assert_beeps("normal! 4]z")
+ call assert_beeps("normal! 4[c")
+ call assert_beeps("normal! 4]c")
+ call assert_beeps("normal! 200%")
+ call assert_beeps("normal! %")
+ call assert_beeps("normal! 2{")
+ call assert_beeps("normal! 2}")
+ call assert_beeps("normal! r\<Right>")
+ call assert_beeps("normal! 8ry")
+ call assert_beeps('normal! "@')
bw!
endfunc
@@ -619,6 +682,13 @@ func Test_normal16_z_scroll_hor()
$put =lineB
1d
+ " Test for zl and zh with a count
+ norm! 0z10l
+ call assert_equal([11, 1], [col('.'), wincol()])
+ norm! z4h
+ call assert_equal([11, 5], [col('.'), wincol()])
+ normal! 2gg
+
" Test for zl
1
norm! 5zl
@@ -741,15 +811,134 @@ func Test_normal17_z_scroll_hor2()
bw!
endfunc
+" Test for commands that scroll the window horizontally. Test with folds.
+" H, M, L, CTRL-E, CTRL-Y, CTRL-U, CTRL-D, PageUp, PageDown commands
+func Test_vert_scroll_cmds()
+ 15new
+ call setline(1, range(1, 100))
+ exe "normal! 30ggz\<CR>"
+ set foldenable
+ 33,36fold
+ 40,43fold
+ 46,49fold
+ let h = winheight(0)
+
+ " Test for H, M and L commands
+ " Top of the screen = 30
+ " Folded lines = 9
+ " Bottom of the screen = 30 + h + 9 - 1
+ normal! 4L
+ call assert_equal(35 + h, line('.'))
+ normal! 4H
+ call assert_equal(33, line('.'))
+
+ " Test for the CTRL-E and CTRL-Y commands with folds
+ %d
+ call setline(1, range(1, 10))
+ 3,5fold
+ exe "normal 6G3\<C-E>"
+ call assert_equal(6, line('w0'))
+ exe "normal 2\<C-Y>"
+ call assert_equal(2, line('w0'))
+
+ " Test for CTRL-Y on a folded line
+ %d
+ call setline(1, range(1, 100))
+ exe (h + 2) .. "," .. (h + 4) .. "fold"
+ exe h + 5
+ normal z-
+ exe "normal \<C-Y>\<C-Y>"
+ call assert_equal(h + 1, line('w$'))
+
+ " Using <PageUp> and <PageDown> in an empty buffer should beep
+ %d
+ call assert_beeps('exe "normal \<PageUp>"')
+ call assert_beeps('exe "normal \<C-B>"')
+ call assert_beeps('exe "normal \<PageDown>"')
+ call assert_beeps('exe "normal \<C-F>"')
+
+ " Test for <C-U> and <C-D> with fold
+ %d
+ call setline(1, range(1, 100))
+ 10,35fold
+ set scroll=10
+ exe "normal \<C-D>"
+ call assert_equal(36, line('.'))
+ exe "normal \<C-D>"
+ call assert_equal(46, line('.'))
+ exe "normal \<C-U>"
+ call assert_equal(36, line('.'))
+ exe "normal \<C-U>"
+ call assert_equal(10, line('.'))
+ exe "normal \<C-U>"
+ call assert_equal(1, line('.'))
+ set scroll&
+
+ " Test for scrolling to the top of the file with <C-U> and a fold
+ 10
+ normal ztL
+ exe "normal \<C-U>\<C-U>"
+ call assert_equal(1, line('w0'))
+
+ " Test for CTRL-D on a folded line
+ %d
+ call setline(1, range(1, 100))
+ 50,100fold
+ 75
+ normal z-
+ exe "normal \<C-D>"
+ call assert_equal(50, line('.'))
+ call assert_equal(100, line('w$'))
+ normal z.
+ let lnum = winline()
+ exe "normal \<C-D>"
+ call assert_equal(lnum, winline())
+ call assert_equal(50, line('.'))
+ normal zt
+ exe "normal \<C-D>"
+ call assert_equal(50, line('w0'))
+
+ set foldenable&
+ close!
+endfunc
+
+" Test for the 'sidescroll' option
+func Test_sidescroll_opt()
+ new
+ 20vnew
+
+ " scroll by 2 characters horizontally
+ set sidescroll=2 nowrap
+ call setline(1, repeat('a', 40))
+ normal g$l
+ call assert_equal(19, screenpos(0, 1, 21).col)
+ normal l
+ call assert_equal(20, screenpos(0, 1, 22).col)
+ normal g0h
+ call assert_equal(2, screenpos(0, 1, 2).col)
+ call assert_equal(20, screenpos(0, 1, 20).col)
+
+ " when 'sidescroll' is 0, cursor positioned at the center
+ set sidescroll=0
+ normal g$l
+ call assert_equal(11, screenpos(0, 1, 21).col)
+ normal g0h
+ call assert_equal(10, screenpos(0, 1, 10).col)
+
+ %bw!
+ set wrap& sidescroll&
+endfunc
+
+" basic tests for foldopen/folddelete
func Test_normal18_z_fold()
- " basic tests for foldopen/folddelete
- if !has("folding")
- return
- endif
+ CheckFeature folding
call Setup_NewWindow()
50
setl foldenable fdm=marker foldlevel=5
+ call assert_beeps('normal! zj')
+ call assert_beeps('normal! zk')
+
" Test for zF
" First fold
norm! 4zF
@@ -1182,6 +1371,9 @@ func Test_normal22_zet()
let a = readfile('Xfile_Test_normal22_zet')
call assert_equal(['1', '2'], a)
+ " Unsupported Z command
+ call assert_beeps('normal! ZW')
+
" Nvim: This sometimes hangs the TSAN build.
" for file in ['Xfile_Test_normal22_zet']
" call delete(file)
@@ -1250,6 +1442,15 @@ func Test_normal23_K()
call assert_match("man --pager=cat 'man'", a)
endif
+ " Error cases
+ call setline(1, '#$#')
+ call assert_fails('normal! ggK', 'E349:')
+ call setline(1, '---')
+ call assert_fails('normal! ggv2lK', 'E349:')
+ call setline(1, ['abc', 'xyz'])
+ call assert_fails("normal! gg2lv2h\<C-]>", 'E426:')
+ call assert_beeps("normal! ggVjK")
+
" clean up
let &keywordprg = k
bw!
@@ -1383,9 +1584,16 @@ func Test_normal27_bracket()
call assert_equal(5, line('.'))
call assert_equal(3, col('.'))
- " No mark after line 21, cursor moves to first non blank on current line
+ " No mark before line 1, cursor moves to first non-blank on current line
+ 1
+ norm! 5|['
+ call assert_equal(' 1 b', getline('.'))
+ call assert_equal(1, line('.'))
+ call assert_equal(3, col('.'))
+
+ " No mark after line 21, cursor moves to first non-blank on current line
21
- norm! $]'
+ norm! 5|]'
call assert_equal(' 21 b', getline('.'))
call assert_equal(21, line('.'))
call assert_equal(3, col('.'))
@@ -1402,12 +1610,40 @@ func Test_normal27_bracket()
call assert_equal(20, line('.'))
call assert_equal(8, col('.'))
+ " No mark before line 1, cursor does not move
+ 1
+ norm! 5|[`
+ call assert_equal(' 1 b', getline('.'))
+ call assert_equal(1, line('.'))
+ call assert_equal(5, col('.'))
+
+ " No mark after line 21, cursor does not move
+ 21
+ norm! 5|]`
+ call assert_equal(' 21 b', getline('.'))
+ call assert_equal(21, line('.'))
+ call assert_equal(5, col('.'))
+
+ " Count too large for [`
+ " cursor moves to first lowercase mark
+ norm! 99[`
+ call assert_equal(' 1 b', getline('.'))
+ call assert_equal(1, line('.'))
+ call assert_equal(7, col('.'))
+
+ " Count too large for ]`
+ " cursor moves to last lowercase mark
+ norm! 99]`
+ call assert_equal(' 20 b', getline('.'))
+ call assert_equal(20, line('.'))
+ call assert_equal(8, col('.'))
+
" clean up
bw!
endfunc
+" Test for ( and ) sentence movements
func Test_normal28_parenthesis()
- " basic testing for ( and )
new
call append(0, ['This is a test. With some sentences!', '', 'Even with a question? And one more. And no sentence here'])
@@ -1425,12 +1661,43 @@ func Test_normal28_parenthesis()
norm! $d(
call assert_equal(['With some sentences!', '', ' ', '', 'This is a long sentence', ''], getline(1, '$'))
+ " Move to the next sentence from a paragraph macro
+ %d
+ call setline(1, ['.LP', 'blue sky!. blue sky.', 'blue sky. blue sky.'])
+ call cursor(1, 1)
+ normal )
+ call assert_equal([2, 1], [line('.'), col('.')])
+ normal )
+ call assert_equal([2, 12], [line('.'), col('.')])
+ normal ((
+ call assert_equal([1, 1], [line('.'), col('.')])
+
+ " It is an error if a next sentence is not found
+ %d
+ call setline(1, '.SH')
+ call assert_beeps('normal )')
+
+ " If only dot is present, don't treat that as a sentence
+ call setline(1, '. This is a sentence.')
+ normal $((
+ call assert_equal(3, col('.'))
+
+ " Jumping to a fold should open the fold
+ call setline(1, ['', '', 'one', 'two', 'three'])
+ set foldenable
+ 2,$fold
+ call feedkeys(')', 'xt')
+ call assert_equal(3, line('.'))
+ call assert_equal(1, foldlevel('.'))
+ call assert_equal(-1, foldclosed('.'))
+ set foldenable&
+
" clean up
bw!
endfunc
-fun! Test_normal29_brace()
- " basic test for { and } movements
+" Test for { and } paragraph movements
+func Test_normal29_brace()
let text =<< trim [DATA]
A paragraph begins after each empty line, and also at each of a set of
paragraph macros, specified by the pairs of characters in the 'paragraphs'
@@ -1583,12 +1850,24 @@ fun! Test_normal29_brace()
" [DATA]
" call assert_equal(expected, getline(1, '$'))
+ " Jumping to a fold should open the fold
+ " %d
+ " call setline(1, ['', 'one', 'two', ''])
+ " set foldenable
+ " 2,$fold
+ " call feedkeys('}', 'xt')
+ " call assert_equal(4, line('.'))
+ " call assert_equal(1, foldlevel('.'))
+ " call assert_equal(-1, foldclosed('.'))
+ " set foldenable&
+
" clean up
set cpo-={
bw!
endfunc
-fun! Test_normal30_changecase()
+" Test for ~ command
+func Test_normal30_changecase()
new
call append(0, 'This is a simple test: äüöß')
norm! 1ggVu
@@ -1608,8 +1887,23 @@ fun! Test_normal30_changecase()
norm! V~
call assert_equal('THIS IS A simple test: äüöss', getline('.'))
- " Turkish ASCII turns to multi-byte. On some systems Turkish locale
- " is available but toupper()/tolower() don't do the right thing.
+ " Test for changing case across lines using 'whichwrap'
+ call setline(1, ['aaaaaa', 'aaaaaa'])
+ normal! gg10~
+ call assert_equal(['AAAAAA', 'aaaaaa'], getline(1, 2))
+ set whichwrap+=~
+ normal! gg10~
+ call assert_equal(['aaaaaa', 'AAAAaa'], getline(1, 2))
+ set whichwrap&
+
+ " clean up
+ bw!
+endfunc
+
+" Turkish ASCII turns to multi-byte. On some systems Turkish locale
+" is available but toupper()/tolower() don't do the right thing.
+func Test_normal_changecase_turkish()
+ new
try
lang tr_TR.UTF-8
set casemap=
@@ -1653,13 +1947,11 @@ fun! Test_normal30_changecase()
" can't use Turkish locale
throw 'Skipped: Turkish locale not available'
endtry
-
- " clean up
- bw!
+ close!
endfunc
-fun! Test_normal31_r_cmd()
- " Test for r command
+" Test for r (replace) command
+func Test_normal31_r_cmd()
new
call append(0, 'This is a simple test: abcd')
exe "norm! 1gg$r\<cr>"
@@ -1678,13 +1970,29 @@ fun! Test_normal31_r_cmd()
exe "norm! 1gg05rf"
call assert_equal('fffffis a', getline(1))
+ " When replacing characters, copy characters from above and below lines
+ " using CTRL-Y and CTRL-E.
+ " Different code paths are used for utf-8 and latin1 encodings
+ set showmatch
+ for enc in ['latin1', 'utf-8']
+ enew!
+ let &encoding = enc
+ call setline(1, [' {a}', 'xxxxxxxxxx', ' [b]'])
+ exe "norm! 2gg5r\<C-Y>l5r\<C-E>"
+ call assert_equal(' {a}x [b]x', getline(2))
+ endfor
+ set showmatch&
+
+ " r command should fail in operator pending mode
+ call assert_beeps('normal! cr')
+
" clean up
set noautoindent
bw!
endfunc
+" Test for g*, g#
func Test_normal32_g_cmd1()
- " Test for g*, g#
new
call append(0, ['abc.x_foo', 'x_foobar.abc'])
1
@@ -1699,11 +2007,12 @@ func Test_normal32_g_cmd1()
bw!
endfunc
-fun! Test_normal33_g_cmd2()
+" Test for g`, g;, g,, g&, gv, gk, gj, gJ, g0, g^, g_, gm, g$, gM, g CTRL-G,
+" gi and gI commands
+func Test_normal33_g_cmd2()
if !has("jumplist")
return
endif
- " Tests for g cmds
call Setup_NewWindow()
" Test for g`
clearjumps
@@ -1715,6 +2024,10 @@ fun! Test_normal33_g_cmd2()
call assert_equal('>', a[-1:])
call assert_equal(1, line('.'))
call assert_equal('1', getline('.'))
+ call cursor(10, 1)
+ norm! g'a
+ call assert_equal('>', a[-1:])
+ call assert_equal(1, line('.'))
" Test for g; and g,
norm! g;
@@ -1745,6 +2058,16 @@ fun! Test_normal33_g_cmd2()
norm! g&
call assert_equal(['11', '22', '33', '44', '55', '66', '77', '88', '9', '110', 'a', 'b', 'c', 'dd'], getline(1, '$'))
+ " Jumping to a fold using gg should open the fold
+ set foldenable
+ set foldopen+=jump
+ 5,8fold
+ call feedkeys('6gg', 'xt')
+ call assert_equal(1, foldlevel('.'))
+ call assert_equal(-1, foldclosed('.'))
+ set foldopen-=jump
+ set foldenable&
+
" Test for gv
%d
call append('$', repeat(['abcdefgh'], 8))
@@ -1756,14 +2079,20 @@ fun! Test_normal33_g_cmd2()
exe "norm! G0\<c-v>4k4ly"
exe "norm! gvood"
call assert_equal(['', 'abfgh', 'abfgh', 'abfgh', 'fgh', 'fgh', 'fgh', 'fgh', 'fgh'], getline(1,'$'))
+ " gv cannot be used in operator pending mode
+ call assert_beeps('normal! cgv')
+ " gv should beep without a previously selected visual area
+ new
+ call assert_beeps('normal! gv')
+ close
" Test for gk/gj
%d
15vsp
set wrap listchars= sbr=
- let lineA='abcdefghijklmnopqrstuvwxyz'
- let lineB='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- let lineC='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
+ let lineA = 'abcdefghijklmnopqrstuvwxyz'
+ let lineB = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ let lineC = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
$put =lineA
$put =lineB
@@ -1796,8 +2125,39 @@ fun! Test_normal33_g_cmd2()
norm! g^yl
call assert_equal(15, col('.'))
call assert_equal('l', getreg(0))
+ call assert_beeps('normal 5g$')
+
+ " Test for g$ with double-width character half displayed
+ vsplit
+ 9wincmd |
+ setlocal nowrap nonumber
+ call setline(2, 'asdfasdfヨ')
+ 2
+ normal 0g$
+ call assert_equal(8, col('.'))
+ 10wincmd |
+ normal 0g$
+ call assert_equal(9, col('.'))
+
+ setlocal signcolumn=yes
+ 11wincmd |
+ normal 0g$
+ call assert_equal(8, col('.'))
+ 12wincmd |
+ normal 0g$
+ call assert_equal(9, col('.'))
+
+ close
- norm! 2ggdd
+ " Test for g_
+ call assert_beeps('normal! 100g_')
+ call setline(2, [' foo ', ' foobar '])
+ normal! 2ggg_
+ call assert_equal(5, col('.'))
+ normal! 2g_
+ call assert_equal(8, col('.'))
+
+ norm! 2ggdG
$put =lineC
" Test for gM
@@ -1839,6 +2199,21 @@ fun! Test_normal33_g_cmd2()
$put ='third line'
norm! gi another word
call assert_equal(['foobar next word another word', 'new line', 'third line'], getline(1,'$'))
+ call setline(1, 'foobar')
+ normal! Ggifirst line
+ call assert_equal('foobarfirst line', getline(1))
+ " Test gi in 'virtualedit' mode with cursor after the end of the line
+ set virtualedit=all
+ call setline(1, 'foo')
+ exe "normal! Abar\<Right>\<Right>\<Right>\<Right>"
+ call setline(1, 'foo')
+ normal! Ggifirst line
+ call assert_equal('foo first line', getline(1))
+ set virtualedit&
+
+ " Test for aboring a g command using CTRL-\ CTRL-G
+ exe "normal! g\<C-\>\<C-G>"
+ call assert_equal('foo first line', getline('.'))
" clean up
bw!
@@ -1860,6 +2235,10 @@ func Test_g_ctrl_g()
let a = execute(":norm! g\<c-g>")
call assert_equal("\n--No lines in buffer--", a)
+ " Test for CTRL-G (same as :file)
+ let a = execute(":norm! \<c-g>")
+ call assert_equal("\n\n\"[No Name]\" --No lines in buffer--", a)
+
call setline(1, ['first line', 'second line'])
" Test g CTRL-g with dos, mac and unix file type.
@@ -1927,8 +2306,8 @@ func Test_g_ctrl_g()
bwipe!
endfunc
-fun! Test_normal34_g_cmd3()
- " Test for g8
+" Test for g8
+func Test_normal34_g_cmd3()
new
let a=execute(':norm! 1G0g8')
call assert_equal("\nNUL", a)
@@ -1945,11 +2324,10 @@ fun! Test_normal34_g_cmd3()
bw!
endfunc
+" Test 8g8 which finds invalid utf8 at or after the cursor.
func Test_normal_8g8()
new
- " Test 8g8 which finds invalid utf8 at or after the cursor.
-
" With invalid byte.
call setline(1, "___\xff___")
norm! 1G08g8g
@@ -1978,8 +2356,8 @@ func Test_normal_8g8()
bw!
endfunc
-fun! Test_normal35_g_cmd4()
- " Test for g<
+" Test for g<
+func Test_normal35_g_cmd4()
" Cannot capture its output,
" probably a bug, therefore, test disabled:
throw "Skipped: output of g< can't be tested currently"
@@ -1988,7 +2366,8 @@ fun! Test_normal35_g_cmd4()
call assert_true(!empty(b), 'failed `execute(g<)`')
endfunc
-fun! Test_normal36_g_cmd5()
+" Test for gp gP go
+func Test_normal36_g_cmd5()
new
call append(0, 'abcdefghijklmnopqrstuvwxyz')
set ff=unix
@@ -2026,8 +2405,8 @@ fun! Test_normal36_g_cmd5()
bw!
endfunc
-fun! Test_normal37_g_cmd6()
- " basic test for gt and gT
+" Test for gt and gT
+func Test_normal37_g_cmd6()
tabnew 1.txt
tabnew 2.txt
tabnew 3.txt
@@ -2050,11 +2429,11 @@ fun! Test_normal37_g_cmd6()
tabclose
endfor
" clean up
- call assert_fails(':tabclose', 'E784')
+ call assert_fails(':tabclose', 'E784:')
endfunc
-fun! Test_normal38_nvhome()
- " Test for <Home> and <C-Home> key
+" Test for <Home> and <C-Home> key
+func Test_normal38_nvhome()
new
call setline(1, range(10))
$
@@ -2069,12 +2448,28 @@ fun! Test_normal38_nvhome()
call assert_equal([0, 5, 1, 0, 1], getcurpos())
exe "norm! \<c-home>"
call assert_equal([0, 1, 1, 0, 1], getcurpos())
+ exe "norm! G\<c-kHome>"
+ call assert_equal([0, 1, 1, 0, 1], getcurpos())
" clean up
bw!
endfunc
-fun! Test_normal39_cw()
+" Test for <End> and <C-End> keys
+func Test_normal_nvend()
+ new
+ call setline(1, map(range(1, 10), '"line" .. v:val'))
+ exe "normal! \<End>"
+ call assert_equal(5, col('.'))
+ exe "normal! 4\<End>"
+ call assert_equal([4, 5], [line('.'), col('.')])
+ exe "normal! \<C-End>"
+ call assert_equal([10, 6], [line('.'), col('.')])
+ close!
+endfunc
+
+" Test for cw cW ce
+func Test_normal39_cw()
" Test for cw and cW on whitespace
" and cpo+=w setting
new
@@ -2095,12 +2490,27 @@ fun! Test_normal39_cw()
norm! 2gg0cwfoo
call assert_equal('foo', getline('.'))
+ call setline(1, 'one; two')
+ call cursor(1, 1)
+ call feedkeys('cwvim', 'xt')
+ call assert_equal('vim; two', getline(1))
+ call feedkeys('0cWone', 'xt')
+ call assert_equal('one two', getline(1))
+ "When cursor is at the end of a word 'ce' will change until the end of the
+ "next word, but 'cw' will change only one character
+ call setline(1, 'one two')
+ call feedkeys('0ecwce', 'xt')
+ call assert_equal('once two', getline(1))
+ call setline(1, 'one two')
+ call feedkeys('0ecely', 'xt')
+ call assert_equal('only', getline(1))
+
" clean up
bw!
endfunc
-fun! Test_normal40_ctrl_bsl()
- " Basic test for CTRL-\ commands
+" Test for CTRL-\ commands
+func Test_normal40_ctrl_bsl()
new
call append(0, 'here are some words')
exe "norm! 1gg0a\<C-\>\<C-N>"
@@ -2118,15 +2528,19 @@ fun! Test_normal40_ctrl_bsl()
exe ":norm! \<c-\>\<c-n>dw"
" set noim
call assert_equal('are some words', getline(1))
- " call assert_false(&insertmode)
+ call assert_false(&insertmode)
+ call assert_beeps("normal! \<C-\>\<C-A>")
+
+ " Using CTRL-\ CTRL-N in cmd window should close the window
+ call feedkeys("q:\<C-\>\<C-N>", 'xt')
+ call assert_equal('', getcmdwintype())
" clean up
bw!
endfunc
-fun! Test_normal41_insert_reg()
- " Test for <c-r>=, <c-r><c-r>= and <c-r><c-o>=
- " in insert mode
+" Test for <c-r>=, <c-r><c-r>= and <c-r><c-o>= in insert mode
+func Test_normal41_insert_reg()
new
set sts=2 sw=2 ts=8 tw=0
call append(0, ["aaa\tbbb\tccc", '', '', ''])
@@ -2144,8 +2558,8 @@ fun! Test_normal41_insert_reg()
bw!
endfunc
+" Test for Ctrl-D and Ctrl-U
func Test_normal42_halfpage()
- " basic test for Ctrl-D and Ctrl-U
call Setup_NewWindow()
call assert_equal(5, &scroll)
exe "norm! \<c-d>"
@@ -2181,92 +2595,6 @@ func Test_normal42_halfpage()
bw!
endfunc
-fun! Test_normal43_textobject1()
- " basic tests for text object aw
- new
- call append(0, ['foobar,eins,foobar', 'foo,zwei,foo '])
- " diw
- norm! 1gg0diw
- call assert_equal([',eins,foobar', 'foo,zwei,foo ', ''], getline(1,'$'))
- " daw
- norm! 2ggEdaw
- call assert_equal([',eins,foobar', 'foo,zwei,', ''], getline(1, '$'))
- %d
- call append(0, ["foo\teins\tfoobar", "foo\tzwei\tfoo "])
- " diW
- norm! 2ggwd2iW
- call assert_equal(['foo eins foobar', 'foo foo ', ''], getline(1,'$'))
- " daW
- norm! 1ggd2aW
- call assert_equal(['foobar', 'foo foo ', ''], getline(1,'$'))
-
- %d
- call append(0, ["foo\teins\tfoobar", "foo\tzwei\tfoo "])
- " aw in visual line mode switches to characterwise mode
- norm! 2gg$Vawd
- call assert_equal(['foo eins foobar', 'foo zwei foo'], getline(1,'$'))
- norm! 1gg$Viwd
- call assert_equal(['foo eins ', 'foo zwei foo'], getline(1,'$'))
-
- " clean up
- bw!
-endfunc
-
-func Test_normal44_textobjects2()
- " basic testing for is and as text objects
- new
- call append(0, ['This is a test. With some sentences!', '', 'Even with a question? And one more. And no sentence here'])
- " Test for dis - does not remove trailing whitespace
- norm! 1gg0dis
- call assert_equal([' With some sentences!', '', 'Even with a question? And one more. And no sentence here', ''], getline(1,'$'))
- " Test for das - removes leading whitespace
- norm! 3ggf?ldas
- call assert_equal([' With some sentences!', '', 'Even with a question? And no sentence here', ''], getline(1,'$'))
- " when used in visual mode, is made characterwise
- norm! 3gg$Visy
- call assert_equal('v', visualmode())
- " reset visualmode()
- norm! 3ggVy
- norm! 3gg$Vasy
- call assert_equal('v', visualmode())
- " basic testing for textobjects a< and at
- %d
- call setline(1, ['<div> ','<a href="foobar" class="foo">xyz</a>',' </div>', ' '])
- " a<
- norm! 1gg0da<
- call assert_equal([' ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
- norm! 1pj
- call assert_equal([' <div>', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
- " at
- norm! d2at
- call assert_equal([' '], getline(1,'$'))
- %d
- call setline(1, ['<div> ','<a href="foobar" class="foo">xyz</a>',' </div>', ' '])
- " i<
- norm! 1gg0di<
- call assert_equal(['<> ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
- norm! 1Pj
- call assert_equal(['<div> ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
- norm! d2it
- call assert_equal(['<div></div>',' '], getline(1,'$'))
- " basic testing for a[ and i[ text object
- %d
- call setline(1, [' ', '[', 'one [two]', 'thre', ']'])
- norm! 3gg0di[
- call assert_equal([' ', '[', ']'], getline(1,'$'))
- call setline(1, [' ', '[', 'one [two]', 'thre', ']'])
- norm! 3gg0ftd2a[
- call assert_equal([' '], getline(1,'$'))
- %d
- " Test for i" when cursor is in front of a quoted object
- call append(0, 'foo "bar"')
- norm! 1gg0di"
- call assert_equal(['foo ""', ''], getline(1,'$'))
-
- " clean up
- bw!
-endfunc
-
func Test_normal45_drop()
if !has('dnd')
" The ~ register does not exist
@@ -2353,7 +2681,7 @@ func Test_normal49_counts()
endfunc
func Test_normal50_commandline()
- if !has("timers") || !has("cmdline_hist") || !has("vertsplit")
+ if !has("timers") || !has("cmdline_hist")
return
endif
func! DoTimerWork(id)
@@ -2415,6 +2743,8 @@ func Test_normal52_rl()
call assert_equal(19, col('.'))
call feedkeys("\<right>", 'tx')
call assert_equal(18, col('.'))
+ call feedkeys("\<left>", 'tx')
+ call assert_equal(19, col('.'))
call feedkeys("\<s-right>", 'tx')
call assert_equal(13, col('.'))
call feedkeys("\<c-right>", 'tx')
@@ -2504,6 +2834,18 @@ func Test_gr_command()
normal 4gro
call assert_equal('ooooecond line', getline(2))
let &cpo = save_cpo
+ normal! ggvegrx
+ call assert_equal('xxxxx line', getline(1))
+ exe "normal! gggr\<C-V>122"
+ call assert_equal('zxxxx line', getline(1))
+ set virtualedit=all
+ normal! 15|grl
+ call assert_equal('zxxxx line l', getline(1))
+ set virtualedit&
+ set nomodifiable
+ call assert_fails('normal! grx', 'E21:')
+ call assert_fails('normal! gRx', 'E21:')
+ set modifiable&
enew!
endfunc
@@ -2526,6 +2868,8 @@ func Test_changelist()
normal g;
call assert_equal([2, 2], [line('.'), col('.')])
call assert_fails('normal g;', 'E662:')
+ new
+ call assert_fails('normal g;', 'E664:')
%bwipe!
let &ul = save_ul
endfunc
@@ -2572,6 +2916,10 @@ endfunc
" Jumping to beginning and end of methods in Java-like languages
func Test_java_motion()
new
+ call assert_beeps('normal! [m')
+ call assert_beeps('normal! ]m')
+ call assert_beeps('normal! [M')
+ call assert_beeps('normal! ]M')
a
Piece of Java
{
@@ -2646,7 +2994,7 @@ Piece of Java
close!
endfunc
-fun! Test_normal_gdollar_cmd()
+func Test_normal_gdollar_cmd()
if !has("jumplist")
return
endif
@@ -2701,10 +3049,11 @@ fun! Test_normal_gdollar_cmd()
bw!
endfunc
-func Test_normal_gk()
+func Test_normal_gk_gj()
" needs 80 column new window
new
vert 80new
+ call assert_beeps('normal gk')
put =[repeat('x',90)..' {{{1', 'x {{{1']
norm! gk
" In a 80 column wide terminal the window will be only 78 char
@@ -2719,12 +3068,12 @@ func Test_normal_gk()
norm! gk
call assert_equal(95, col('.'))
call assert_equal(95, virtcol('.'))
- bw!
- bw!
+ %bw!
" needs 80 column new window
new
vert 80new
+ call assert_beeps('normal gj')
set number
set numberwidth=10
set cpoptions+=n
@@ -2743,9 +3092,191 @@ func Test_normal_gk()
call assert_equal(1, col('.'))
norm! gj
call assert_equal(76, col('.'))
- bw!
- bw!
- set cpoptions& number& numberwidth&
+ " When 'nowrap' is set, gk and gj behave like k and j
+ set nowrap
+ normal! gk
+ call assert_equal([2, 76], [line('.'), col('.')])
+ normal! gj
+ call assert_equal([3, 76], [line('.'), col('.')])
+ %bw!
+ set cpoptions& number& numberwidth& wrap&
+endfunc
+
+" Test for cursor movement with '-' in 'cpoptions'
+func Test_normal_cpo_minus()
+ throw 'Skipped: Nvim does not support cpoptions flag "-"'
+ new
+ call setline(1, ['foo', 'bar', 'baz'])
+ let save_cpo = &cpo
+ set cpo+=-
+ call assert_beeps('normal 10j')
+ call assert_equal(1, line('.'))
+ normal G
+ call assert_beeps('normal 10k')
+ call assert_equal(3, line('.'))
+ call assert_fails(10, 'E16:')
+ let &cpo = save_cpo
+ close!
+endfunc
+
+" Test for displaying dollar when changing text ('$' flag in 'cpoptions')
+func Test_normal_cpo_dollar()
+ throw 'Skipped: use test/functional/legacy/cpoptions_spec.lua'
+ new
+ let g:Line = ''
+ func SaveFirstLine()
+ let g:Line = Screenline(1)
+ return ''
+ endfunc
+ inoremap <expr> <buffer> <F2> SaveFirstLine()
+ call test_override('redraw_flag', 1)
+ set cpo+=$
+ call setline(1, 'one two three')
+ redraw!
+ exe "normal c2w\<F2>vim"
+ call assert_equal('one tw$ three', g:Line)
+ call assert_equal('vim three', getline(1))
+ set cpo-=$
+ call test_override('ALL', 0)
+ delfunc SaveFirstLine
+ %bw!
+endfunc
+
+" Test for using : to run a multi-line Ex command in operator pending mode
+func Test_normal_yank_with_excmd()
+ new
+ call setline(1, ['foo', 'bar', 'baz'])
+ let @a = ''
+ call feedkeys("\"ay:if v:true\<CR>normal l\<CR>endif\<CR>", 'xt')
+ call assert_equal('f', @a)
+ close!
+endfunc
+
+" Test for supplying a count to a normal-mode command across a cursorhold call
+func Test_normal_cursorhold_with_count()
+ throw 'Skipped: Nvim removed <CursorHold> key'
+ func s:cHold()
+ let g:cHold_Called += 1
+ endfunc
+ new
+ augroup normalcHoldTest
+ au!
+ au CursorHold <buffer> call s:cHold()
+ augroup END
+ let g:cHold_Called = 0
+ call feedkeys("3\<CursorHold>2ix", 'xt')
+ call assert_equal(1, g:cHold_Called)
+ call assert_equal(repeat('x', 32), getline(1))
+ augroup normalcHoldTest
+ au!
+ augroup END
+ au! normalcHoldTest
+ close!
+ delfunc s:cHold
+endfunc
+
+" Test for using a count and a command with CTRL-W
+func Test_wincmd_with_count()
+ call feedkeys("\<C-W>12n", 'xt')
+ call assert_equal(12, winheight(0))
+endfunc
+
+" Test for 'b', 'B' 'ge' and 'gE' commands
+func Test_horiz_motion()
+ new
+ normal! gg
+ call assert_beeps('normal! b')
+ call assert_beeps('normal! B')
+ call assert_beeps('normal! gE')
+ call assert_beeps('normal! ge')
+ " <S-Backspace> moves one word left and <C-Backspace> moves one WORD left
+ call setline(1, 'one ,two ,three')
+ exe "normal! $\<S-BS>"
+ call assert_equal(11, col('.'))
+ exe "normal! $\<C-BS>"
+ call assert_equal(10, col('.'))
+ close!
+endfunc
+
+" Test for using a : command in operator pending mode
+func Test_normal_colon_op()
+ new
+ call setline(1, ['one', 'two'])
+ call assert_beeps("normal! Gc:d\<CR>")
+ close!
+endfunc
+
+" Test for 'w' and 'b' commands
+func Test_normal_word_move()
+ new
+ call setline(1, ['foo bar a', '', 'foo bar b'])
+ " copy a single character word at the end of a line
+ normal 1G$yw
+ call assert_equal('a', @")
+ " copy a single character word at the end of a file
+ normal G$yw
+ call assert_equal('b', @")
+ " check for a word movement handling an empty line properly
+ normal 1G$vwy
+ call assert_equal("a\n\n", @")
+
+ " copy using 'b' command
+ %d
+ " non-empty blank line at the start of file
+ call setline(1, [' ', 'foo bar'])
+ normal 2Gyb
+ call assert_equal(" \n", @")
+ " try to copy backwards from the start of the file
+ call setline(1, ['one two', 'foo bar'])
+ call assert_beeps('normal ggyb')
+ " 'b' command should stop at an empty line
+ call setline(1, ['one two', '', 'foo bar'])
+ normal 3Gyb
+ call assert_equal("\n", @")
+ normal 3Gy2b
+ call assert_equal("two\n", @")
+ " 'b' command should not stop at a non-empty blank line
+ call setline(1, ['one two', ' ', 'foo bar'])
+ normal 3Gyb
+ call assert_equal("two\n ", @")
+
+ close!
+endfunc
+
+" Test for 'scrolloff' with a long line that doesn't fit in the screen
+func Test_normal_scroloff()
+ 10new
+ 80vnew
+ call setline(1, repeat('a', 1000))
+ set scrolloff=10
+ normal gg10gj
+ call assert_equal(8, winline())
+ normal 10gj
+ call assert_equal(10, winline())
+ normal 10gk
+ call assert_equal(3, winline())
+ set scrolloff&
+ close!
+endfunc
+
+" Test for vertical scrolling with CTRL-F and CTRL-B with a long line
+func Test_normal_vert_scroll_longline()
+ 10new
+ 80vnew
+ call setline(1, range(1, 10))
+ call append(5, repeat('a', 1000))
+ exe "normal gg\<C-F>"
+ call assert_equal(6, line('.'))
+ exe "normal \<C-F>\<C-F>"
+ call assert_equal(11, line('.'))
+ call assert_equal(1, winline())
+ exe "normal \<C-B>"
+ call assert_equal(10, line('.'))
+ call assert_equal(3, winline())
+ exe "normal \<C-B>\<C-B>"
+ call assert_equal(5, line('.'))
+ call assert_equal(5, winline())
+ close!
endfunc
" Some commands like yy, cc, dd, >>, << and !! accept a count after
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index f73cd5f5e6..1f003041e6 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -238,6 +238,12 @@ func Test_set_completion()
call feedkeys(":set di\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"set dictionary diff diffexpr diffopt digraph directory display', @:)
+ call feedkeys(":setlocal di\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"setlocal dictionary diff diffexpr diffopt digraph directory display', @:)
+
+ call feedkeys(":setglobal di\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"setglobal dictionary diff diffexpr diffopt digraph directory display', @:)
+
" Expand boolan options. When doing :set no<Tab>
" vim displays the options names without "no" but completion uses "no...".
call feedkeys(":set nodi\<C-A>\<C-B>\"\<CR>", 'tx')
@@ -268,6 +274,7 @@ func Test_set_completion()
call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:)
+
set tags&
" Expand values for 'filetype'
@@ -283,9 +290,10 @@ func Test_set_errors()
call assert_fails('set regexpengine=3', 'E474:')
call assert_fails('set history=10001', 'E474:')
call assert_fails('set numberwidth=21', 'E474:')
- call assert_fails('set colorcolumn=-a')
- call assert_fails('set colorcolumn=a')
- call assert_fails('set colorcolumn=1,')
+ call assert_fails('set colorcolumn=-a', 'E474:')
+ call assert_fails('set colorcolumn=a', 'E474:')
+ call assert_fails('set colorcolumn=1,', 'E474:')
+ call assert_fails('set colorcolumn=1;', 'E474:')
call assert_fails('set cmdheight=-1', 'E487:')
call assert_fails('set cmdwinheight=-1', 'E487:')
if has('conceal')
@@ -336,9 +344,13 @@ func Test_set_errors()
call assert_fails('set guicursor=i-ci,r-cr:h', 'E545:')
call assert_fails('set guicursor=i-ci', 'E545:')
call assert_fails('set guicursor=x', 'E545:')
+ call assert_fails('set guicursor=x:', 'E546:')
call assert_fails('set guicursor=r-cr:horx', 'E548:')
call assert_fails('set guicursor=r-cr:hor0', 'E549:')
endif
+ if has('mouseshape')
+ call assert_fails('se mouseshape=i-r:x', 'E547:')
+ endif
call assert_fails('set backupext=~ patchmode=~', 'E589:')
call assert_fails('set winminheight=10 winheight=9', 'E591:')
call assert_fails('set winminwidth=10 winwidth=9', 'E592:')
@@ -454,9 +466,11 @@ func Test_set_one_column()
endfunc
func Test_set_values()
- " The file is only generated when running "make test" in the src directory.
+ " opt_test.vim is generated from ../optiondefs.h using gen_opt_test.vim
if filereadable('opt_test.vim')
source opt_test.vim
+ else
+ throw 'Skipped: opt_test.vim does not exist'
endif
endfunc
@@ -623,7 +637,7 @@ func Test_copy_winopt()
call assert_equal(4,&numberwidth)
bw!
- set nohidden
+ set hidden&
endfunc
func Test_shortmess_F()
@@ -729,7 +743,26 @@ func Test_buftype()
call setline(1, ['L1'])
set buftype=nowrite
call assert_fails('write', 'E382:')
- close!
+
+ " for val in ['', 'nofile', 'nowrite', 'acwrite', 'quickfix', 'help', 'terminal', 'prompt', 'popup']
+ for val in ['', 'nofile', 'nowrite', 'acwrite', 'quickfix', 'help', 'prompt']
+ exe 'set buftype=' .. val
+ call writefile(['something'], 'XBuftype')
+ call assert_fails('write XBuftype', 'E13:', 'with buftype=' .. val)
+ endfor
+
+ call delete('XBuftype')
+ bwipe!
+endfunc
+
+" Test for the 'shell' option
+func Test_shell()
+ throw 'Skipped: Nvim does not have :shell'
+ CheckUnix
+ let save_shell = &shell
+ set shell=
+ call assert_fails('shell', 'E91:')
+ let &shell = save_shell
endfunc
" Test for the 'shellquote' option
@@ -745,6 +778,29 @@ func Test_shellquote()
call assert_match(': "#echo Hello#"', v)
endfunc
+" Test for the 'rightleftcmd' option
+func Test_rightleftcmd()
+ CheckFeature rightleft
+ set rightleft
+ set rightleftcmd
+
+ let g:l = []
+ func AddPos()
+ call add(g:l, screencol())
+ return ''
+ endfunc
+ cmap <expr> <F2> AddPos()
+
+ call feedkeys("/\<F2>abc\<Left>\<F2>\<Right>\<Right>\<F2>" ..
+ \ "\<Left>\<F2>\<Esc>", 'xt')
+ call assert_equal([&co - 1, &co - 4, &co - 2, &co - 3], g:l)
+
+ cunmap <F2>
+ unlet g:l
+ set rightleftcmd&
+ set rightleft&
+endfunc
+
" Test for setting option values using v:false and v:true
func Test_opt_boolean()
set number&
@@ -759,6 +815,47 @@ func Test_opt_boolean()
set number&
endfunc
+" Test for the 'window' option
+func Test_window_opt()
+ " Needs only one open widow
+ %bw!
+ call setline(1, range(1, 8))
+ set window=5
+ exe "normal \<C-F>"
+ call assert_equal(4, line('w0'))
+ exe "normal \<C-F>"
+ call assert_equal(7, line('w0'))
+ exe "normal \<C-F>"
+ call assert_equal(8, line('w0'))
+ exe "normal \<C-B>"
+ call assert_equal(5, line('w0'))
+ exe "normal \<C-B>"
+ call assert_equal(2, line('w0'))
+ exe "normal \<C-B>"
+ call assert_equal(1, line('w0'))
+ set window=1
+ exe "normal gg\<C-F>"
+ call assert_equal(2, line('w0'))
+ exe "normal \<C-F>"
+ call assert_equal(3, line('w0'))
+ exe "normal \<C-B>"
+ call assert_equal(2, line('w0'))
+ exe "normal \<C-B>"
+ call assert_equal(1, line('w0'))
+ enew!
+ set window&
+endfunc
+
+" Test for the 'winminheight' option
+func Test_opt_winminheight()
+ only!
+ let &winheight = &lines + 4
+ call assert_fails('let &winminheight = &lines + 2', 'E36:')
+ call assert_true(&winminheight <= &lines)
+ set winminheight&
+ set winheight&
+endfunc
+
func Test_opt_winminheight_term()
" See test/functional/legacy/options_spec.lua
CheckRunVimInTerminal
@@ -802,6 +899,16 @@ func Test_opt_winminheight_term_tabs()
call delete('Xwinminheight')
endfunc
+" Test for the 'winminwidth' option
+func Test_opt_winminwidth()
+ only!
+ let &winwidth = &columns + 4
+ call assert_fails('let &winminwidth = &columns + 2', 'E36:')
+ call assert_true(&winminwidth <= &columns)
+ set winminwidth&
+ set winwidth&
+endfunc
+
" Test for setting option value containing spaces with isfname+=32
func Test_isfname_with_options()
set isfname+=32
diff --git a/src/nvim/testdir/test_perl.vim b/src/nvim/testdir/test_perl.vim
index b911a982f9..558d0a5d6b 100644
--- a/src/nvim/testdir/test_perl.vim
+++ b/src/nvim/testdir/test_perl.vim
@@ -1,8 +1,8 @@
" Tests for Perl interface
-if !has('perl') || has('win32')
- finish
-endif
+source check.vim
+CheckFeature perl
+CheckNotMSWindows
" FIXME: RunTest don't see any error when Perl abort...
perl $SIG{__WARN__} = sub { die "Unexpected warnings from perl: @_" };
diff --git a/src/nvim/testdir/test_plus_arg_edit.vim b/src/nvim/testdir/test_plus_arg_edit.vim
index e31680e7b6..c52044d064 100644
--- a/src/nvim/testdir/test_plus_arg_edit.vim
+++ b/src/nvim/testdir/test_plus_arg_edit.vim
@@ -18,7 +18,7 @@ func Test_edit_bad()
e! ++enc=utf8 Xfile
call assert_equal('[?][?][???][??]', getline(1))
- e! ++enc=utf8 ++bad=_ Xfile
+ e! ++encoding=utf8 ++bad=_ Xfile
call assert_equal('[_][_][___][__]', getline(1))
e! ++enc=utf8 ++bad=drop Xfile
@@ -32,3 +32,16 @@ func Test_edit_bad()
bw!
call delete('Xfile')
endfunc
+
+" Test for ++bin and ++nobin arguments
+func Test_binary_arg()
+ new
+ edit ++bin Xfile1
+ call assert_equal(1, &binary)
+ edit ++nobin Xfile2
+ call assert_equal(0, &binary)
+ call assert_fails('edit ++binabc Xfile3', 'E474:')
+ close!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim
index 9a31f61582..a5e4be49f4 100644
--- a/src/nvim/testdir/test_popup.vim
+++ b/src/nvim/testdir/test_popup.vim
@@ -349,19 +349,17 @@ func DummyCompleteOne(findstart, base)
endif
endfunc
-" Test that nothing happens if the 'completefunc' opens
-" a new window (no completion, no crash)
+" Test that nothing happens if the 'completefunc' tries to open
+" a new window (fails to open window, continues)
func Test_completefunc_opens_new_window_one()
new
let winid = win_getid()
setlocal completefunc=DummyCompleteOne
call setline(1, 'one')
/^one
- call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E839:')
- call assert_notequal(winid, win_getid())
- q!
+ call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E565:')
call assert_equal(winid, win_getid())
- call assert_equal('', getline(1))
+ call assert_equal('oneDEF', getline(1))
q!
endfunc
@@ -384,11 +382,11 @@ func Test_completefunc_opens_new_window_two()
setlocal completefunc=DummyCompleteTwo
call setline(1, 'two')
/^two
- call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E764:')
- call assert_notequal(winid, win_getid())
- q!
+ call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E565:')
call assert_equal(winid, win_getid())
- call assert_equal('two', getline(1))
+ " v8.2.1919 hasn't been ported yet
+ " call assert_equal('twodef', getline(1))
+ call assert_equal('twoDEF', getline(1))
q!
endfunc
@@ -657,8 +655,8 @@ func Test_complete_func_mess()
set completefunc=MessComplete
new
call setline(1, 'Ju')
- call feedkeys("A\<c-x>\<c-u>/\<esc>", 'tx')
- call assert_equal('Oct/Oct', getline(1))
+ call assert_fails('call feedkeys("A\<c-x>\<c-u>/\<esc>", "tx")', 'E565:')
+ call assert_equal('Jan/', getline(1))
bwipe!
set completefunc=
endfunc
@@ -913,7 +911,7 @@ func Test_popup_complete_backwards_ctrl_p()
bwipe!
endfunc
-fun! Test_complete_o_tab()
+func Test_complete_o_tab()
CheckFunction test_override
let s:o_char_pressed = 0
@@ -922,7 +920,7 @@ fun! Test_complete_o_tab()
let s:o_char_pressed = 0
call feedkeys("\<c-x>\<c-n>", 'i')
endif
- endf
+ endfunc
set completeopt=menu,noselect
new
@@ -941,7 +939,21 @@ fun! Test_complete_o_tab()
bwipe!
set completeopt&
delfunc s:act_on_text_changed
-endf
+endfunc
+
+func Test_menu_only_exists_in_terminal()
+ if !exists(':tlmenu') || has('gui_running')
+ return
+ endif
+ tlnoremenu &Edit.&Paste<Tab>"+gP <C-W>"+
+ aunmenu *
+ try
+ popup Edit
+ call assert_false(1, 'command should have failed')
+ catch
+ call assert_exception('E328:')
+ endtry
+endfunc
func Test_popup_complete_info_01()
new
diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim
index a46ef8b3fe..97af3699a8 100644
--- a/src/nvim/testdir/test_put.vim
+++ b/src/nvim/testdir/test_put.vim
@@ -114,6 +114,16 @@ func Test_put_p_indent_visual()
bwipe!
endfunc
+" Test for deleting all the contents of a buffer with a put
+func Test_put_visual_delete_all_lines()
+ new
+ call setline(1, ['one', 'two', 'three'])
+ let @r = ''
+ normal! VG"rgp
+ call assert_equal(1, line('$'))
+ close!
+endfunc
+
func Test_gp_with_count_leaves_cursor_at_end()
new
call setline(1, '<---->')
diff --git a/src/nvim/testdir/test_python2.vim b/src/nvim/testdir/test_python2.vim
index ae8bc57c7f..745b7da086 100644
--- a/src/nvim/testdir/test_python2.vim
+++ b/src/nvim/testdir/test_python2.vim
@@ -1,9 +1,8 @@
" Test for python 2 commands.
" TODO: move tests from test86.in here.
-if !has('python')
- finish
-endif
+source check.vim
+CheckFeature python
func Test_pydo()
" Check deleting lines does not trigger ml_get error.
diff --git a/src/nvim/testdir/test_python3.vim b/src/nvim/testdir/test_python3.vim
index f6a1942e24..69f5f6dcc0 100644
--- a/src/nvim/testdir/test_python3.vim
+++ b/src/nvim/testdir/test_python3.vim
@@ -1,9 +1,8 @@
" Test for python 3 commands.
" TODO: move tests from test87.in here.
-if !has('python3')
- finish
-endif
+source check.vim
+CheckFeature python3
func Test_py3do()
" Check deleting lines does not trigger an ml_get error.
diff --git a/src/nvim/testdir/test_pyx2.vim b/src/nvim/testdir/test_pyx2.vim
index 6a8ebf3da0..eee825fa9b 100644
--- a/src/nvim/testdir/test_pyx2.vim
+++ b/src/nvim/testdir/test_pyx2.vim
@@ -1,8 +1,7 @@
" Test for pyx* commands and functions with Python 2.
-if !has('python')
- finish
-endif
+source check.vim
+CheckFeature python
set pyx=2
let s:py2pattern = '^2\.[0-7]\.\d\+'
diff --git a/src/nvim/testdir/test_pyx3.vim b/src/nvim/testdir/test_pyx3.vim
index 2044af3abe..db39f5134a 100644
--- a/src/nvim/testdir/test_pyx3.vim
+++ b/src/nvim/testdir/test_pyx3.vim
@@ -1,9 +1,8 @@
" Test for pyx* commands and functions with Python 3.
set pyx=3
-if !has('python3')
- finish
-endif
+source check.vim
+CheckFeature python3
let s:py2pattern = '^2\.[0-7]\.\d\+'
let s:py3pattern = '^3\.\d\+\.\d\+'
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 5826666cbb..ddd4229f17 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -301,6 +301,23 @@ func XwindowTests(cchar)
call assert_equal(12, winwidth(0))
Xclose
+ " Horizontally or vertically splitting the quickfix window should create a
+ " normal window/buffer
+ Xopen
+ wincmd s
+ call assert_equal(0, getwininfo(win_getid())[0].quickfix)
+ call assert_equal(0, getwininfo(win_getid())[0].loclist)
+ call assert_notequal('quickfix', &buftype)
+ close
+ Xopen
+ wincmd v
+ call assert_equal(0, getwininfo(win_getid())[0].quickfix)
+ call assert_equal(0, getwininfo(win_getid())[0].loclist)
+ call assert_notequal('quickfix', &buftype)
+ close
+ Xopen
+ Xclose
+
if a:cchar == 'c'
" Opening the quickfix window in multiple tab pages should reuse the
" quickfix buffer
@@ -499,13 +516,14 @@ func Xtest_browse(cchar)
\ 'RegularLine2']
Xfirst
+ call assert_fails('-5Xcc', 'E16:')
call assert_fails('Xprev', 'E553')
call assert_fails('Xpfile', 'E553')
Xnfile
- call assert_equal('Xqftestfile2', bufname('%'))
+ call assert_equal('Xqftestfile2', @%)
call assert_equal(10, line('.'))
Xpfile
- call assert_equal('Xqftestfile1', bufname('%'))
+ call assert_equal('Xqftestfile1', @%)
call assert_equal(6, line('.'))
5Xcc
call assert_equal(5, g:Xgetlist({'idx':0}).idx)
@@ -521,7 +539,7 @@ func Xtest_browse(cchar)
call assert_equal(6, g:Xgetlist({'idx':0}).idx)
Xlast
Xprev
- call assert_equal('Xqftestfile2', bufname('%'))
+ call assert_equal('Xqftestfile2', @%)
call assert_equal(11, line('.'))
call assert_fails('Xnext', 'E553')
call assert_fails('Xnfile', 'E553')
@@ -534,14 +552,14 @@ func Xtest_browse(cchar)
endif
call assert_equal(6, g:Xgetlist({'idx':0}).idx)
Xrewind
- call assert_equal('Xqftestfile1', bufname('%'))
+ call assert_equal('Xqftestfile1', @%)
call assert_equal(5, line('.'))
10Xnext
- call assert_equal('Xqftestfile2', bufname('%'))
+ call assert_equal('Xqftestfile2', @%)
call assert_equal(11, line('.'))
10Xprev
- call assert_equal('Xqftestfile1', bufname('%'))
+ call assert_equal('Xqftestfile1', @%)
call assert_equal(5, line('.'))
" Jumping to an error from the error window using cc command
@@ -552,14 +570,23 @@ func Xtest_browse(cchar)
Xopen
10Xcc
call assert_equal(11, line('.'))
- call assert_equal('Xqftestfile2', bufname('%'))
+ call assert_equal('Xqftestfile2', @%)
+ Xopen
+ call cursor(2, 1)
+ if a:cchar == 'c'
+ .cc
+ else
+ .ll
+ endif
+ call assert_equal(6, line('.'))
+ call assert_equal('Xqftestfile1', @%)
" Jumping to an error from the error window (when only the error window is
" present)
Xopen | only
Xlast 1
call assert_equal(5, line('.'))
- call assert_equal('Xqftestfile1', bufname('%'))
+ call assert_equal('Xqftestfile1', @%)
Xexpr ""
call assert_fails('Xnext', 'E42:')
@@ -1934,7 +1961,7 @@ func Test_switchbuf()
copen | only
cfirst
call assert_equal(1, tabpagenr())
- call assert_equal('Xqftestfile1', bufname(''))
+ call assert_equal('Xqftestfile1', @%)
" If opening a file changes 'switchbuf', then the new value should be
" retained.
@@ -2750,7 +2777,7 @@ func Test_cwindow_jump()
wincmd b
cfirst
call assert_equal(2, winnr())
- call assert_equal('F1', bufname(''))
+ call assert_equal('F1', @%)
enew | only
exe 'sb' bnum
exe 'botright sb' bnum
@@ -2840,7 +2867,7 @@ func XvimgrepTests(cchar)
edit +3 Xtestfile2
Xvimgrep +\cemacs+j Xtestfile1
let l = g:Xgetlist()
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal('Editor:Emacs EmAcS', l[0].text)
" Test for unloading a buffer after vimgrep searched the buffer
@@ -2882,6 +2909,21 @@ func Test_vimgrep_incsearch()
set noincsearch
endfunc
+" Test vimgrep with the last search pattern not set
+func Test_vimgrep_with_no_last_search_pat()
+ let lines =<< trim [SCRIPT]
+ call assert_fails('vimgrep // *', 'E35:')
+ call writefile(v:errors, 'Xresult')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
" Test vimgrep without swap file
func Test_vimgrep_without_swap_file()
let lines =<< trim [SCRIPT]
@@ -3082,20 +3124,80 @@ func Test_file_from_copen()
endfunc
func Test_resize_from_copen()
+ augroup QF_Test
+ au!
+ au FileType qf resize 5
+ augroup END
+ try
+ " This should succeed without any exception. No other buffers are
+ " involved in the autocmd.
+ copen
+ finally
augroup QF_Test
- au!
- au FileType qf resize 5
+ au!
augroup END
- try
- " This should succeed without any exception. No other buffers are
- " involved in the autocmd.
- copen
- finally
- augroup QF_Test
- au!
- augroup END
- augroup! QF_Test
- endtry
+ augroup! QF_Test
+ endtry
+endfunc
+
+func Test_vimgrep_with_textlock()
+ new
+
+ " Simple way to execute something with "textlock" set.
+ " Check that vimgrep without jumping can be executed.
+ au InsertCharPre * vimgrep /RunTheTest/j runtest.vim
+ normal ax
+ let qflist = getqflist()
+ call assert_true(len(qflist) > 0)
+ call assert_match('RunTheTest', qflist[0].text)
+ call setqflist([], 'r')
+ au! InsertCharPre
+
+ " Check that vimgrepadd without jumping can be executed.
+ au InsertCharPre * vimgrepadd /RunTheTest/j runtest.vim
+ normal ax
+ let qflist = getqflist()
+ call assert_true(len(qflist) > 0)
+ call assert_match('RunTheTest', qflist[0].text)
+ call setqflist([], 'r')
+ au! InsertCharPre
+
+ " Check that lvimgrep without jumping can be executed.
+ au InsertCharPre * lvimgrep /RunTheTest/j runtest.vim
+ normal ax
+ let qflist = getloclist(0)
+ call assert_true(len(qflist) > 0)
+ call assert_match('RunTheTest', qflist[0].text)
+ call setloclist(0, [], 'r')
+ au! InsertCharPre
+
+ " Check that lvimgrepadd without jumping can be executed.
+ au InsertCharPre * lvimgrepadd /RunTheTest/j runtest.vim
+ normal ax
+ let qflist = getloclist(0)
+ call assert_true(len(qflist) > 0)
+ call assert_match('RunTheTest', qflist[0].text)
+ call setloclist(0, [], 'r')
+ au! InsertCharPre
+
+ " trying to jump will give an error
+ au InsertCharPre * vimgrep /RunTheTest/ runtest.vim
+ call assert_fails('normal ax', 'E565:')
+ au! InsertCharPre
+
+ au InsertCharPre * vimgrepadd /RunTheTest/ runtest.vim
+ call assert_fails('normal ax', 'E565:')
+ au! InsertCharPre
+
+ au InsertCharPre * lvimgrep /RunTheTest/ runtest.vim
+ call assert_fails('normal ax', 'E565:')
+ au! InsertCharPre
+
+ au InsertCharPre * lvimgrepadd /RunTheTest/ runtest.vim
+ call assert_fails('normal ax', 'E565:')
+ au! InsertCharPre
+
+ bwipe!
endfunc
" Tests for the quickfix buffer b:changedtick variable
@@ -3494,7 +3596,7 @@ func Xqfjump_tests(cchar)
Xopen | only
2Xnext
call assert_equal(3, g:Xgetlist({'idx' : 0}).idx)
- call assert_equal('F3', bufname('%'))
+ call assert_equal('F3', @%)
Xnext
call assert_equal(7, col('.'))
Xnext
@@ -4188,20 +4290,20 @@ func Xjumpto_first_error_test(cchar)
" Test for cexpr/lexpr
enew
Xexpr l
- call assert_equal('Xtestfile1', bufname(''))
+ call assert_equal('Xtestfile1', @%)
call assert_equal(2, line('.'))
" Test for cfile/lfile
enew
call writefile(l, 'Xerr')
Xfile Xerr
- call assert_equal('Xtestfile1', bufname(''))
+ call assert_equal('Xtestfile1', @%)
call assert_equal(2, line('.'))
" Test for cbuffer/lbuffer
edit Xerr
Xbuffer
- call assert_equal('Xtestfile1', bufname(''))
+ call assert_equal('Xtestfile1', @%)
call assert_equal(2, line('.'))
call delete('Xerr')
@@ -4226,7 +4328,7 @@ func Xautocmd_changelist(cchar)
autocmd QuickFixCmdPost * Xolder
call writefile(['Xtestfile2:4:Line4'], 'Xerr')
Xfile Xerr
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal(4, line('.'))
autocmd! QuickFixCmdPost
@@ -4237,7 +4339,7 @@ func Xautocmd_changelist(cchar)
call writefile(['Xtestfile2:4:Line4'], 'Xerr')
edit Xerr
Xbuffer
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal(4, line('.'))
autocmd! QuickFixCmdPost
@@ -4246,7 +4348,7 @@ func Xautocmd_changelist(cchar)
Xexpr 'Xtestfile1:2:Line2'
autocmd QuickFixCmdPost * Xolder
Xexpr 'Xtestfile2:4:Line4'
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal(4, line('.'))
autocmd! QuickFixCmdPost
@@ -4257,7 +4359,7 @@ func Xautocmd_changelist(cchar)
Xexpr 'Xtestfile1:2:Line2'
autocmd QuickFixCmdPost * Xolder
silent Xgrep Line5 Xtestfile2
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal(5, line('.'))
autocmd! QuickFixCmdPost
endif
@@ -4267,7 +4369,7 @@ func Xautocmd_changelist(cchar)
Xexpr 'Xtestfile1:2:Line2'
autocmd QuickFixCmdPost * Xolder
silent Xvimgrep Line5 Xtestfile2
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal(5, line('.'))
autocmd! QuickFixCmdPost
@@ -4391,6 +4493,20 @@ func Test_splitview()
call assert_equal(0, getloclist(0, {'winid' : 0}).winid)
new | only
+ " Using :split or :vsplit from a quickfix window should behave like a :new
+ " or a :vnew command
+ copen
+ split
+ call assert_equal(3, winnr('$'))
+ let l = getwininfo()
+ call assert_equal([0, 0, 1], [l[0].quickfix, l[1].quickfix, l[2].quickfix])
+ close
+ copen
+ vsplit
+ let l = getwininfo()
+ call assert_equal([0, 0, 1], [l[0].quickfix, l[1].quickfix, l[2].quickfix])
+ new | only
+
call delete('Xtestfile1')
call delete('Xtestfile2')
endfunc
@@ -4568,7 +4684,7 @@ func Test_winonly_autocmd()
" positioned correctly.
ll 3
call assert_equal(loclistid, getloclist(0, {'id' : 0}).id)
- call assert_equal('Xtest1', bufname(''))
+ call assert_equal('Xtest1', @%)
call assert_equal(15, line('.'))
" Cleanup
autocmd! WinEnter
@@ -4629,51 +4745,51 @@ func Xtest_below(cchar)
Xexpr ["X1:5:3:L5", "X2:5:2:L5", "X2:10:3:L10", "X2:15:4:L15", "X3:3:5:L3"]
edit +7 X2
Xabove
- call assert_equal(['X2', 5], [bufname(''), line('.')])
+ call assert_equal(['X2', 5], [@%, line('.')])
call assert_fails('Xabove', 'E553:')
normal 7G
Xbefore
- call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 2], [@%, line('.'), col('.')])
call assert_fails('Xbefore', 'E553:')
normal 2j
Xbelow
- call assert_equal(['X2', 10], [bufname(''), line('.')])
+ call assert_equal(['X2', 10], [@%, line('.')])
normal 7G
Xafter
- call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 3], [@%, line('.'), col('.')])
" Last error in this file
Xbelow 99
- call assert_equal(['X2', 15], [bufname(''), line('.')])
+ call assert_equal(['X2', 15], [@%, line('.')])
call assert_fails('Xbelow', 'E553:')
normal gg
Xafter 99
- call assert_equal(['X2', 15, 4], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 4], [@%, line('.'), col('.')])
call assert_fails('Xafter', 'E553:')
" First error in this file
Xabove 99
- call assert_equal(['X2', 5], [bufname(''), line('.')])
+ call assert_equal(['X2', 5], [@%, line('.')])
call assert_fails('Xabove', 'E553:')
normal G
Xbefore 99
- call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 2], [@%, line('.'), col('.')])
call assert_fails('Xbefore', 'E553:')
normal gg
Xbelow 2
- call assert_equal(['X2', 10], [bufname(''), line('.')])
+ call assert_equal(['X2', 10], [@%, line('.')])
normal gg
Xafter 2
- call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 3], [@%, line('.'), col('.')])
normal G
Xabove 2
- call assert_equal(['X2', 10], [bufname(''), line('.')])
+ call assert_equal(['X2', 10], [@%, line('.')])
normal G
Xbefore 2
- call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 3], [@%, line('.'), col('.')])
edit X4
call assert_fails('Xabove', 'E42:')
@@ -4697,45 +4813,45 @@ func Xtest_below(cchar)
\ "X2:15:1:L15_1", "X2:15:2:L15_2", "X2:15:3:L15_3", "X3:3:L3"]
edit +1 X2
Xbelow 2
- call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 1], [@%, line('.'), col('.')])
normal 1G
Xafter 2
- call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 2], [@%, line('.'), col('.')])
normal gg
Xbelow 99
- call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 1], [@%, line('.'), col('.')])
normal gg
Xafter 99
- call assert_equal(['X2', 15, 3], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 3], [@%, line('.'), col('.')])
normal G
Xabove 2
- call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 1], [@%, line('.'), col('.')])
normal G
Xbefore 2
- call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 2], [@%, line('.'), col('.')])
normal G
Xabove 99
- call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 1], [@%, line('.'), col('.')])
normal G
Xbefore 99
- call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 1], [@%, line('.'), col('.')])
normal 10G
Xabove
- call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 1], [@%, line('.'), col('.')])
normal 10G$
2Xbefore
- call assert_equal(['X2', 10, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 2], [@%, line('.'), col('.')])
normal 10G
Xbelow
- call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 1], [@%, line('.'), col('.')])
normal 9G
5Xafter
- call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 2], [@%, line('.'), col('.')])
" Invalid range
if a:cchar == 'c'
diff --git a/src/nvim/testdir/test_quotestar.vim b/src/nvim/testdir/test_quotestar.vim
index 6e6f91362b..93865869fa 100644
--- a/src/nvim/testdir/test_quotestar.vim
+++ b/src/nvim/testdir/test_quotestar.vim
@@ -1,10 +1,9 @@
" *-register (quotestar) tests
-if !has('clipboard')
- finish
-endif
-
source shared.vim
+source check.vim
+
+CheckFeature clipboard_working
func Do_test_quotestar_for_macunix()
if empty(exepath('pbcopy')) || empty(exepath('pbpaste'))
diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index 45e60a6d44..82d250e8b3 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -122,7 +122,10 @@ endfunc
" Tests for regexp patterns without multi-byte support.
func Test_regexp_single_line_pat()
" tl is a List of Lists with:
- " regexp engine
+ " regexp engines to test
+ " 0 - test with 'regexpengine' values 0 and 1
+ " 1 - test with 'regexpengine' values 0 and 2
+ " 2 - test with 'regexpengine' values 0, 1 and 2
" regexp pattern
" text to test the pattern on
" expected match (optional)
@@ -143,6 +146,8 @@ func Test_regexp_single_line_pat()
call add(tl, [2, 'c*', 'abdef', ''])
call add(tl, [2, 'bc\+', 'abccccdef', 'bcccc'])
call add(tl, [2, 'bc\+', 'abdef']) " no match
+ " match newline character in a string
+ call add(tl, [2, 'o\nb', "foo\nbar", "o\nb"])
" operator \|
call add(tl, [2, 'a\|ab', 'cabd', 'a']) " alternation is ordered
@@ -566,6 +571,9 @@ func Test_regexp_single_line_pat()
" Test \%V atom
call add(tl, [2, '\%>70vGesamt', 'Jean-Michel Charlier & Victor Hubinon\Gesamtausgabe [Salleck] Buck Danny {Jean-Michel Charlier & Victor Hubinon}\Gesamtausgabe', 'Gesamt'])
+ " Test for ignoring case and matching repeated characters
+ call add(tl, [2, '\cb\+', 'aAbBbBcC', 'bBbB'])
+
" Run the tests
for t in tl
let re = t[0]
@@ -625,6 +633,14 @@ endfunc
" Tests for multi-line regexp patterns without multi-byte support.
func Test_regexp_multiline_pat()
+ " tl is a List of Lists with:
+ " regexp engines to test
+ " 0 - test with 'regexpengine' values 0 and 1
+ " 1 - test with 'regexpengine' values 0 and 2
+ " 2 - test with 'regexpengine' values 0, 1 and 2
+ " regexp pattern
+ " List with text to test the pattern on
+ " List with the expected match
let tl = []
" back references
@@ -634,6 +650,70 @@ func Test_regexp_multiline_pat()
" line breaks
call add(tl, [2, '\S.*\nx', ['abc', 'def', 'ghi', 'xjk', 'lmn'], ['abc', 'def', 'XXjk', 'lmn']])
+ " Any single character or end-of-line
+ call add(tl, [2, '\_.\+', ['a', 'b', 'c'], ['XX']])
+ " Any identifier or end-of-line
+ call add(tl, [2, '\_i\+', ['a', 'b', ';', '2'], ['XX;XX']])
+ " Any identifier but excluding digits or end-of-line
+ call add(tl, [2, '\_I\+', ['a', 'b', ';', '2'], ['XX;XX2XX']])
+ " Any keyword or end-of-line
+ call add(tl, [2, '\_k\+', ['a', 'b', '=', '2'], ['XX=XX']])
+ " Any keyword but excluding digits or end-of-line
+ call add(tl, [2, '\_K\+', ['a', 'b', '=', '2'], ['XX=XX2XX']])
+ " Any filename character or end-of-line
+ call add(tl, [2, '\_f\+', ['a', 'b', '.', '5'], ['XX']])
+ " Any filename character but excluding digits or end-of-line
+ call add(tl, [2, '\_F\+', ['a', 'b', '.', '5'], ['XX5XX']])
+ " Any printable character or end-of-line
+ call add(tl, [2, '\_p\+', ['a', 'b', '=', '4'], ['XX']])
+ " Any printable character excluding digits or end-of-line
+ call add(tl, [2, '\_P\+', ['a', 'b', '=', '4'], ['XX4XX']])
+ " Any whitespace character or end-of-line
+ call add(tl, [2, '\_s\+', [' ', ' ', 'a', 'b'], ['XXaXXbXX']])
+ " Any non-whitespace character or end-of-line
+ call add(tl, [2, '\_S\+', [' ', ' ', 'a', 'b'], [' XX XX']])
+ " Any decimal digit or end-of-line
+ call add(tl, [2, '\_d\+', ['1', 'a', '2', 'b', '3'], ['XXaXXbXX']])
+ " Any non-decimal digit or end-of-line
+ call add(tl, [2, '\_D\+', ['1', 'a', '2', 'b', '3'], ['1XX2XX3XX']])
+ " Any hexadecimal digit or end-of-line
+ call add(tl, [2, '\_x\+', ['1', 'a', 'g', '9', '8'], ['XXgXX']])
+ " Any non-hexadecimal digit or end-of-line
+ call add(tl, [2, '\_X\+', ['1', 'a', 'g', '9', '8'], ['1XXaXX9XX8XX']])
+ " Any octal digit or end-of-line
+ call add(tl, [2, '\_o\+', ['0', '7', '8', '9', '0'], ['XX8XX9XX']])
+ " Any non-octal digit or end-of-line
+ call add(tl, [2, '\_O\+', ['0', '7', '8', '9', '0'], ['0XX7XX0XX']])
+ " Any word character or end-of-line
+ call add(tl, [2, '\_w\+', ['A', 'B', '=', 'C', 'D'], ['XX=XX']])
+ " Any non-word character or end-of-line
+ call add(tl, [2, '\_W\+', ['A', 'B', '=', 'C', 'D'], ['AXXBXXCXXDXX']])
+ " Any head-of-word character or end-of-line
+ call add(tl, [2, '\_h\+', ['a', '1', 'b', '2', 'c'], ['XX1XX2XX']])
+ " Any non-head-of-word character or end-of-line
+ call add(tl, [2, '\_H\+', ['a', '1', 'b', '2', 'c'], ['aXXbXXcXX']])
+ " Any alphabetic character or end-of-line
+ call add(tl, [2, '\_a\+', ['a', '1', 'b', '2', 'c'], ['XX1XX2XX']])
+ " Any non-alphabetic character or end-of-line
+ call add(tl, [2, '\_A\+', ['a', '1', 'b', '2', 'c'], ['aXXbXXcXX']])
+ " Any lowercase character or end-of-line
+ call add(tl, [2, '\_l\+', ['a', 'A', 'b', 'B'], ['XXAXXBXX']])
+ " Any non-lowercase character or end-of-line
+ call add(tl, [2, '\_L\+', ['a', 'A', 'b', 'B'], ['aXXbXX']])
+ " Any uppercase character or end-of-line
+ call add(tl, [2, '\_u\+', ['a', 'A', 'b', 'B'], ['aXXbXX']])
+ " Any non-uppercase character or end-of-line
+ call add(tl, [2, '\_U\+', ['a', 'A', 'b', 'B'], ['XXAXXBXX']])
+ " Collection or end-of-line
+ call add(tl, [2, '\_[a-z]\+', ['a', 'A', 'b', 'B'], ['XXAXXBXX']])
+ " start of line anywhere in the text
+ call add(tl, [2, 'one\zs\_s*\_^\zetwo',
+ \ ['', 'one', ' two', 'one', '', 'two'],
+ \ ['', 'one', ' two', 'oneXXtwo']])
+ " end of line anywhere in the text
+ call add(tl, [2, 'one\zs\_$\_s*two',
+ \ ['', 'one', ' two', 'one', '', 'two'], ['', 'oneXX', 'oneXX']])
+
" Check that \_[0-9] matching EOL does not break a following \>
call add(tl, [2, '\<\(\(25\_[0-5]\|2\_[0-4]\_[0-9]\|\_[01]\?\_[0-9]\_[0-9]\?\)\.\)\{3\}\(25\_[0-5]\|2\_[0-4]\_[0-9]\|\_[01]\?\_[0-9]\_[0-9]\?\)\>', ['', 'localnet/192.168.0.1', ''], ['', 'localnet/XX', '']])
@@ -649,7 +729,7 @@ func Test_regexp_multiline_pat()
let before = t[2]
let after = t[3]
for engine in [0, 1, 2]
- if engine == 2 && re == 0 || engine == 1 && re ==1
+ if engine == 2 && re == 0 || engine == 1 && re == 1
continue
endif
let &regexpengine = engine
@@ -697,9 +777,8 @@ func Test_lookbehind_across_line()
bwipe!
endfunc
-" Check matching Visual area
-func Test_matching_visual_area()
- new
+" Test for the \%V atom (match inside the visual area)
+func Regex_Match_Visual_Area()
call append(0, ['Visual:', 'thexe the thexethe', 'andaxand andaxand',
\ 'oooxofor foroxooo', 'oooxofor foroxooo'])
call cursor(1, 1)
@@ -708,12 +787,22 @@ func Test_matching_visual_area()
exe "normal jfx\<C-V>fxj:s/\\%Vo/O/g\<CR>"
call assert_equal(['Visual:', 'thexE thE thExethe', 'AndAxAnd AndAxAnd',
\ 'oooxOfOr fOrOxooo', 'oooxOfOr fOrOxooo', ''], getline(1, '$'))
+ %d
+endfunc
+
+" Check matching Visual area
+func Test_matching_visual_area()
+ new
+ set regexpengine=1
+ call Regex_Match_Visual_Area()
+ set regexpengine=2
+ call Regex_Match_Visual_Area()
+ set regexpengine&
bwipe!
endfunc
" Check matching marks
-func Test_matching_marks()
- new
+func Regex_Mark()
call append(0, ['', '', '', 'Marks:', 'asdfSasdfsadfEasdf', 'asdfSas',
\ 'dfsadfEasdf', '', '', '', '', ''])
call cursor(4, 1)
@@ -721,6 +810,15 @@ func Test_matching_marks()
exe "normal jfSmsj0fEme:.-4,.+6s/.\\%>'s\\_.*\\%<'e../again/\<CR>"
call assert_equal(['', '', '', 'Marks:', 'asdfhereasdf', 'asdfagainasdf',
\ '', '', '', '', '', ''], getline(1, '$'))
+ %d
+endfunc
+
+func Test_matching_marks()
+ new
+ set regexpengine=1
+ call Regex_Mark()
+ set regexpengine=2
+ call Regex_Mark()
bwipe!
endfunc
@@ -761,8 +859,7 @@ func Test_matching_curpos()
endfunc
" Test for matching the start and end of a buffer
-func Test_start_end_of_buffer_match()
- new
+func Regex_start_end_buffer()
call setline(1, repeat(['vim edit'], 20))
/\%^
call assert_equal([0, 1, 1, 0], getpos('.'))
@@ -772,6 +869,15 @@ func Test_start_end_of_buffer_match()
call assert_equal([0, 20, 8, 0], getpos('.'))
exe "normal 6gg/..\\%$\<CR>"
call assert_equal([0, 20, 7, 0], getpos('.'))
+ %d
+endfunc
+
+func Test_start_end_of_buffer_match()
+ new
+ set regexpengine=1
+ call Regex_start_end_buffer()
+ set regexpengine=2
+ call Regex_start_end_buffer()
bwipe!
endfunc
@@ -784,10 +890,20 @@ endfunc
" Check for detecting error
func Test_regexp_error()
- set regexpengine=2
- call assert_fails("call matchlist('x x', ' \\ze*')", 'E888:')
- call assert_fails("call matchlist('x x', ' \\zs*')", 'E888:')
- set re&
+ call assert_fails("call matchlist('x x', '\\%#=1 \\zs*')", 'E888:')
+ call assert_fails("call matchlist('x x', '\\%#=1 \\ze*')", 'E888:')
+ call assert_fails("call matchlist('x x', '\\%#=2 \\zs*')", 'E888:')
+ call assert_fails("call matchlist('x x', '\\%#=2 \\ze*')", 'E888:')
+ call assert_fails('exe "normal /\\%#=1\\%[x\\%[x]]\<CR>"', 'E369:')
+endfunc
+
+" Test for using the last substitute string pattern (~)
+func Test_regexp_last_subst_string()
+ new
+ s/bar/baz/e
+ call assert_equal(matchstr("foo\nbaz\nbar", "\\%#=1\~"), "baz")
+ call assert_equal(matchstr("foo\nbaz\nbar", "\\%#=2\~"), "baz")
+ close!
endfunc
" Check patterns matching cursor position.
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index b852cfd22f..52e745438d 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -416,6 +416,20 @@ func Test_put_reg_restart_mode()
bwipe!
endfunc
+" Test for executing a register using :@ command
+func Test_execute_register()
+ call setreg('r', [])
+ call assert_beeps('@r')
+ let i = 1
+ let @q = 'let i+= 1'
+ @q
+ @
+ call assert_equal(3, i)
+
+ " cannot execute a register in operator pending mode
+ call assert_beeps('normal! c@r')
+endfunc
+
" Test for getting register info
func Test_get_reginfo()
enew
@@ -670,6 +684,16 @@ func Test_insert_small_delete()
bwipe!
endfunc
+" Record in insert mode using CTRL-O
+func Test_record_in_insert_mode()
+ new
+ let @r = ''
+ call setline(1, ['foo'])
+ call feedkeys("i\<C-O>qrbaz\<C-O>q", 'xt')
+ call assert_equal('baz', @r)
+ bwipe!
+endfunc
+
func Test_record_in_select_mode()
new
call setline(1, 'text')
diff --git a/src/nvim/testdir/test_reltime.vim b/src/nvim/testdir/test_reltime.vim
index 37b9e783c6..b381f1ddbb 100644
--- a/src/nvim/testdir/test_reltime.vim
+++ b/src/nvim/testdir/test_reltime.vim
@@ -1,8 +1,8 @@
" Tests for reltime()
-if !has('reltime') || !has('float')
- finish
-endif
+source check.vim
+CheckFeature reltime
+CheckFeature float
func Test_reltime()
let now = reltime()
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 454c956996..3d1bbfb726 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -19,9 +19,9 @@ func Test_search_cmdline()
set noincsearch
:1
call feedkeys("/foobar\<cr>", 'tx')
- call feedkeys("/the\<cr>",'tx')
+ call feedkeys("/the\<cr>", 'tx')
call assert_equal('the', @/)
- call feedkeys("/thes\<C-P>\<C-P>\<cr>",'tx')
+ call feedkeys("/thes\<C-P>\<C-P>\<cr>", 'tx')
call assert_equal('foobar', @/)
" Test 2
@@ -655,10 +655,49 @@ func Test_search_cmdline7()
bw!
endfunc
-" Tests for regexp with various magic settings
-func Test_search_regexp()
- enew!
+func Test_search_cmdline8()
+ " Highlighting is cleared in all windows
+ " since hls applies to all windows
+ CheckOption incsearch
+ CheckFeature terminal
+ CheckNotGui
+ if has("win32")
+ throw "Skipped: Bug with sending <ESC> to terminal window not fixed yet"
+ endif
+ let h = winheight(0)
+ if h < 3
+ return
+ endif
+ " Prepare buffer text
+ let lines = ['abb vim vim vi', 'vimvivim']
+ call writefile(lines, 'Xsearch.txt')
+ let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile', 'Xsearch.txt'], {'term_rows': 3})
+
+ call WaitForAssert({-> assert_equal(lines, [term_getline(buf, 1), term_getline(buf, 2)])})
+
+ call term_sendkeys(buf, ":set incsearch hlsearch\<cr>")
+ call term_sendkeys(buf, ":14vsp\<cr>")
+ call term_sendkeys(buf, "/vim\<cr>")
+ call term_sendkeys(buf, "/b\<esc>")
+ call term_sendkeys(buf, "gg0")
+ call TermWait(buf, 250)
+ let screen_line = term_scrape(buf, 1)
+ let [a0,a1,a2,a3] = [screen_line[3].attr, screen_line[4].attr,
+ \ screen_line[18].attr, screen_line[19].attr]
+ call assert_notequal(a0, a1)
+ call assert_notequal(a0, a3)
+ call assert_notequal(a1, a2)
+ call assert_equal(a0, a2)
+ call assert_equal(a1, a3)
+ " clean up
+ call delete('Xsearch.txt')
+
+ bwipe!
+endfunc
+
+" Tests for regexp with various magic settings
+func Run_search_regexp_magic_opt()
put ='1 a aa abb abbccc'
exe 'normal! /a*b\{2}c\+/e' . "\<CR>"
call assert_equal([0, 2, 17, 0], getpos('.'))
@@ -693,6 +732,18 @@ func Test_search_regexp()
exe 'normal! /\V[ab]\(\[xy]\)\1' . "\<CR>"
call assert_equal([0, 9, 7, 0], getpos('.'))
+ %d
+endfunc
+
+func Test_search_regexp()
+ enew!
+
+ set regexpengine=1
+ call Run_search_regexp_magic_opt()
+ set regexpengine=2
+ call Run_search_regexp_magic_opt()
+ set regexpengine&
+
set undolevels=100
put ='9 foobar'
put =''
@@ -700,12 +751,12 @@ func Test_search_regexp()
normal G
exe 'normal! dv?bar?' . "\<CR>"
call assert_equal('9 foo', getline('.'))
- call assert_equal([0, 10, 5, 0], getpos('.'))
- call assert_equal(10, line('$'))
+ call assert_equal([0, 2, 5, 0], getpos('.'))
+ call assert_equal(2, line('$'))
normal u
call assert_equal('9 foobar', getline('.'))
- call assert_equal([0, 10, 6, 0], getpos('.'))
- call assert_equal(11, line('$'))
+ call assert_equal([0, 2, 6, 0], getpos('.'))
+ call assert_equal(3, line('$'))
set undolevels&
enew!
@@ -1433,7 +1484,7 @@ func Test_large_hex_chars2()
endfunc
func Test_one_error_msg()
- " This was also giving an internal error
+ " This was also giving an internal error
call assert_fails('call search(" \\((\\v[[=P=]]){185}+ ")', 'E871:')
endfunc
@@ -1478,6 +1529,20 @@ func Test_search_match_at_curpos()
close!
endfunc
+" Test for error cases with the search() function
+func Test_search_errors()
+ call assert_fails("call search('pat', [])", 'E730:')
+ call assert_fails("call search('pat', 'b', {})", 'E728:')
+ call assert_fails("call search('pat', 'b', 1, [])", 'E745:')
+ call assert_fails("call search('pat', 'ns')", 'E475:')
+ call assert_fails("call search('pat', 'mr')", 'E475:')
+
+ new
+ call setline(1, ['foo', 'bar'])
+ call assert_fails('call feedkeys("/foo/;/bar/;\<CR>", "tx")', 'E386:')
+ bwipe!
+endfunc
+
func Test_search_display_pattern()
new
call setline(1, ['foo', 'bar', 'foobar'])
@@ -1541,6 +1606,160 @@ func Test_search_special()
exe "norm /\x80PS"
endfunc
+" Test for command failures when the last search pattern is not set.
+" Need to run this in a new vim instance where last search pattern is not set.
+func Test_search_with_no_last_pat()
+ let lines =<< trim [SCRIPT]
+ call assert_fails("normal i\<C-R>/\e", 'E35:')
+ call assert_fails("exe '/'", 'E35:')
+ call assert_fails("exe '?'", 'E35:')
+ call assert_fails("/", 'E35:')
+ call assert_fails("?", 'E35:')
+ call assert_fails("normal n", 'E35:')
+ call assert_fails("normal N", 'E35:')
+ call assert_fails("normal gn", 'E35:')
+ call assert_fails("normal gN", 'E35:')
+ call assert_fails("normal cgn", 'E35:')
+ call assert_fails("normal cgN", 'E35:')
+ let p = []
+ let p = @/
+ call assert_equal('', p)
+ call assert_fails("normal :\<C-R>/", 'E35:')
+ call assert_fails("//p", 'E35:')
+ call assert_fails(";//p", 'E35:')
+ call assert_fails("??p", 'E35:')
+ call assert_fails(";??p", 'E35:')
+ call assert_fails('g//p', 'E476:')
+ call assert_fails('v//p', 'E476:')
+ call writefile(v:errors, 'Xresult')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+" Test for using tilde (~) atom in search. This should use the last used
+" substitute pattern
+func Test_search_tilde_pat()
+ let lines =<< trim [SCRIPT]
+ set regexpengine=1
+ call assert_fails('exe "normal /~\<CR>"', 'E33:')
+ call assert_fails('exe "normal ?~\<CR>"', 'E33:')
+ set regexpengine=2
+ call assert_fails('exe "normal /~\<CR>"', 'E383:')
+ call assert_fails('exe "normal ?~\<CR>"', 'E383:')
+ set regexpengine&
+ call writefile(v:errors, 'Xresult')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+" Test for searching a pattern that is not present with 'nowrapscan'
+func Test_search_pat_not_found()
+ new
+ set nowrapscan
+ let @/ = '1abcxyz2'
+ call assert_fails('normal n', 'E385:')
+ call assert_fails('normal N', 'E384:')
+ set wrapscan&
+ close
+endfunc
+
+" Test for v:searchforward variable
+func Test_searchforward_var()
+ new
+ call setline(1, ['foo', '', 'foo'])
+ call cursor(2, 1)
+ let @/ = 'foo'
+ let v:searchforward = 0
+ normal N
+ call assert_equal(3, line('.'))
+ call cursor(2, 1)
+ let v:searchforward = 1
+ normal N
+ call assert_equal(1, line('.'))
+ close!
+endfunc
+
+" Test for invalid regular expressions
+func Test_invalid_regexp()
+ set regexpengine=1
+ call assert_fails("call search(repeat('\\(.\\)', 10))", 'E51:')
+ call assert_fails("call search('\\%(')", 'E53:')
+ call assert_fails("call search('\\(')", 'E54:')
+ call assert_fails("call search('\\)')", 'E55:')
+ call assert_fails("call search('x\\@#')", 'E59:')
+ call assert_fails('call search(''\v%(%(%(%(%(%(%(%(%(%(%(a){1}){1}){1}){1}){1}){1}){1}){1}){1}){1}){1}'')', 'E60:')
+ call assert_fails("call search('a\\+*')", 'E61:')
+ call assert_fails("call search('\\_m')", 'E63:')
+ call assert_fails("call search('\\+')", 'E64:')
+ call assert_fails("call search('\\1')", 'E65:')
+ call assert_fails("call search('\\z\\(\\)')", 'E66:')
+ call assert_fails("call search('\\z2')", 'E67:')
+ call assert_fails("call search('\\zx')", 'E68:')
+ call assert_fails("call search('\\%[ab')", 'E69:')
+ call assert_fails("call search('\\%[]')", 'E70:')
+ call assert_fails("call search('\\%a')", 'E71:')
+ call assert_fails("call search('ab\\%[\\(cd\\)]')", 'E369:')
+ call assert_fails("call search('ab\\%[\\%(cd\\)]')", 'E369:')
+ set regexpengine=2
+ call assert_fails("call search('\\_')", 'E865:')
+ call assert_fails("call search('\\+')", 'E866:')
+ call assert_fails("call search('\\zx')", 'E867:')
+ call assert_fails("call search('\\%a')", 'E867:')
+ call assert_fails("call search('x\\@#')", 'E869:')
+ call assert_fails("call search(repeat('\\(.\\)', 10))", 'E872:')
+ call assert_fails("call search('\\_m')", 'E877:')
+ call assert_fails("call search('\\%(')", 'E53:')
+ call assert_fails("call search('\\(')", 'E54:')
+ call assert_fails("call search('\\)')", 'E55:')
+ call assert_fails("call search('\\z\\(\\)')", 'E66:')
+ call assert_fails("call search('\\%[ab')", 'E69:')
+ call assert_fails("call search('\\%[]')", 'E70:')
+ call assert_fails("call search('\\%9999999999999999999999999999v')", 'E951:')
+ set regexpengine&
+ call assert_fails("call search('\\%#=3ab')", 'E864:')
+endfunc
+
+" Test for searching with 'smartcase' and 'ignorecase'
+func Test_search_smartcase()
+ new
+ call setline(1, ['', 'Hello'])
+ set noignorecase nosmartcase
+ call assert_fails('exe "normal /\\a\\_.\\(.*\\)O\<CR>"', 'E486:')
+
+ set ignorecase nosmartcase
+ exe "normal /\\a\\_.\\(.*\\)O\<CR>"
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ call cursor(1, 1)
+ set ignorecase smartcase
+ call assert_fails('exe "normal /\\a\\_.\\(.*\\)O\<CR>"', 'E486:')
+
+ exe "normal /\\a\\_.\\(.*\\)o\<CR>"
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ " Test for using special atoms with 'smartcase'
+ call setline(1, ['', ' Hello\ '])
+ call cursor(1, 1)
+ call feedkeys('/\_.\%(\uello\)\' .. "\<CR>", 'xt')
+ call assert_equal([2, 4], [line('.'), col('.')])
+
+ set ignorecase& smartcase&
+ close!
+endfun
+
" Test 'smartcase' with utf-8.
func Test_search_smartcase_utf8()
new
@@ -1560,6 +1779,102 @@ func Test_search_smartcase_utf8()
close!
endfunc
+" Test searching past the end of a file
+func Test_search_past_eof()
+ new
+ call setline(1, ['Line'])
+ exe "normal /\\n\\zs\<CR>"
+ call assert_equal([1, 4], [line('.'), col('.')])
+ close!
+endfunc
+
+" Test for various search offsets
+func Test_search_offset()
+ " With /e, for a match in the first column of a line, the cursor should be
+ " placed at the end of the previous line.
+ new
+ call setline(1, ['one two', 'three four'])
+ call search('two\_.', 'e')
+ call assert_equal([1, 7], [line('.'), col('.')])
+
+ " with cursor at the beginning of the file, use /s+1
+ call cursor(1, 1)
+ exe "normal /two/s+1\<CR>"
+ call assert_equal([1, 6], [line('.'), col('.')])
+
+ " with cursor at the end of the file, use /e-1
+ call cursor(2, 10)
+ exe "normal ?three?e-1\<CR>"
+ call assert_equal([2, 4], [line('.'), col('.')])
+
+ " line offset - after the last line
+ call cursor(1, 1)
+ exe "normal /three/+1\<CR>"
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ " line offset - before the first line
+ call cursor(2, 1)
+ exe "normal ?one?-1\<CR>"
+ call assert_equal([1, 1], [line('.'), col('.')])
+
+ " character offset - before the first character in the file
+ call cursor(2, 1)
+ exe "normal ?one?s-1\<CR>"
+ call assert_equal([1, 1], [line('.'), col('.')])
+ call cursor(2, 1)
+ exe "normal ?one?e-3\<CR>"
+ call assert_equal([1, 1], [line('.'), col('.')])
+
+ " character offset - after the last character in the file
+ call cursor(1, 1)
+ exe "normal /four/s+4\<CR>"
+ call assert_equal([2, 10], [line('.'), col('.')])
+ call cursor(1, 1)
+ exe "normal /four/e+1\<CR>"
+ call assert_equal([2, 10], [line('.'), col('.')])
+
+ close!
+endfunc
+
+" Test for searching for matching parenthesis using %
+func Test_search_match_paren()
+ new
+ call setline(1, "abc(def')'ghi'('jk'\\t'lm)no")
+ " searching for a matching parenthesis should skip single quoted characters
+ call cursor(1, 4)
+ normal %
+ call assert_equal([1, 25], [line('.'), col('.')])
+ normal %
+ call assert_equal([1, 4], [line('.'), col('.')])
+ call cursor(1, 5)
+ normal ])
+ call assert_equal([1, 25], [line('.'), col('.')])
+ call cursor(1, 24)
+ normal [(
+ call assert_equal([1, 4], [line('.'), col('.')])
+
+ " matching parenthesis in 'virtualedit' mode with cursor after the eol
+ call setline(1, 'abc(defgh)')
+ set virtualedit=all
+ normal 20|%
+ call assert_equal(4, col('.'))
+ set virtualedit&
+ close!
+endfunc
+
+" Test for searching a pattern and stopping before a specified line
+func Test_search_stopline()
+ new
+ call setline(1, ['', '', '', 'vim'])
+ call assert_equal(0, search('vim', 'n', 3))
+ call assert_equal(4, search('vim', 'n', 4))
+ call setline(1, ['vim', '', '', ''])
+ call cursor(4, 1)
+ call assert_equal(0, search('vim', 'bn', 2))
+ call assert_equal(1, search('vim', 'bn', 1))
+ close!
+endfunc
+
func Test_incsearch_highlighting_newline()
CheckRunVimInTerminal
CheckOption incsearch
diff --git a/src/nvim/testdir/test_sha256.vim b/src/nvim/testdir/test_sha256.vim
index 76d1306836..f6f430b04e 100644
--- a/src/nvim/testdir/test_sha256.vim
+++ b/src/nvim/testdir/test_sha256.vim
@@ -1,8 +1,8 @@
" Tests for the sha256() function.
-if !has('cryptv') || !exists('*sha256')
- finish
-endif
+source check.vim
+CheckFeature cryptv
+CheckFunction sha256
function Test_sha256()
" test for empty string:
diff --git a/src/nvim/testdir/test_smartindent.vim b/src/nvim/testdir/test_smartindent.vim
index f3650a9ac4..e2d028e828 100644
--- a/src/nvim/testdir/test_smartindent.vim
+++ b/src/nvim/testdir/test_smartindent.vim
@@ -21,9 +21,7 @@ endfunc
func Test_smartindent_has_no_effect()
new
exe "normal! i\<Tab>one\<Esc>"
- set noautoindent
- set smartindent
- set indentexpr=
+ setlocal noautoindent smartindent indentexpr=
exe "normal! Gotwo\<Esc>"
call assert_equal("\ttwo", getline("$"))
@@ -32,16 +30,13 @@ func Test_smartindent_has_no_effect()
call assert_equal("three", getline("$"))
delfunction! MyIndent
- set autoindent&
- set smartindent&
- set indentexpr&
bwipe!
endfunc
" Test for inserting '{' and '} with smartindent
func Test_smartindent_braces()
new
- set smartindent shiftwidth=4
+ setlocal smartindent shiftwidth=4
call setline(1, [' if (a)', "\tif (b)", "\t return 1"])
normal 2ggO{
normal 3ggA {
@@ -57,7 +52,62 @@ func Test_smartindent_braces()
\ "\t}",
\ ' }'
\ ], getline(1, '$'))
- set si& sw& ai&
+ close!
+endfunc
+
+" Test for adding a new line before and after comments with smartindent
+func Test_si_add_line_around_comment()
+ new
+ setlocal smartindent shiftwidth=4
+ call setline(1, [' A', '# comment1', '# comment2'])
+ exe "normal GoC\<Esc>2GOB"
+ call assert_equal([' A', ' B', '# comment1', '# comment2', ' C'],
+ \ getline(1, '$'))
+ close!
+endfunc
+
+" After a C style comment, indent for a following line should line up with the
+" line containing the start of the comment.
+func Test_si_indent_after_c_comment()
+ new
+ setlocal smartindent shiftwidth=4 fo+=ro
+ exe "normal i\<C-t>/*\ncomment\n/\n#define FOOBAR\n75\<Esc>ggOabc"
+ normal 3jOcont
+ call assert_equal([' abc', ' /*', ' * comment', ' * cont',
+ \ ' */', '#define FOOBAR', ' 75'], getline(1, '$'))
+ close!
+endfunc
+
+" Test for indenting a statement after a if condition split across lines
+func Test_si_if_cond_split_across_lines()
+ new
+ setlocal smartindent shiftwidth=4
+ exe "normal i\<C-t>if (cond1 &&\n\<C-t>cond2) {\ni = 10;\n}"
+ call assert_equal([' if (cond1 &&', "\t cond2) {", "\ti = 10;",
+ \ ' }'], getline(1, '$'))
+ close!
+endfunc
+
+" Test for inserting lines before and after a one line comment
+func Test_si_one_line_comment()
+ new
+ setlocal smartindent shiftwidth=4
+ exe "normal i\<C-t>abc;\n\<C-t>/* comment */"
+ normal oi = 10;
+ normal kOj = 1;
+ call assert_equal([' abc;', "\tj = 1;", "\t/* comment */", "\ti = 10;"],
+ \ getline(1, '$'))
+ close!
+endfunc
+
+" Test for smartindent with a comment continued across multiple lines
+func Test_si_comment_line_continuation()
+ new
+ setlocal smartindent shiftwidth=4
+ call setline(1, ['# com1', '# com2 \', ' contd', '# com3', ' xyz'])
+ normal ggOabc
+ call assert_equal([' abc', '# com1', '# com2 \', ' contd', '# com3',
+ \ ' xyz'], getline(1, '$'))
close!
endfunc
diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim
index 540c73a772..9895ad754c 100644
--- a/src/nvim/testdir/test_sort.vim
+++ b/src/nvim/testdir/test_sort.vim
@@ -1489,6 +1489,22 @@ func Test_sort_last_search_pat()
close!
endfunc
+" Test for :sort with no last search pattern
+func Test_sort_with_no_last_search_pat()
+ let lines =<< trim [SCRIPT]
+ call setline(1, ['3b', '1c', '2a'])
+ call assert_fails('sort //', 'E35:')
+ call writefile(v:errors, 'Xresult')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
" Test for retaining marks across a :sort
func Test_sort_with_marks()
new
diff --git a/src/nvim/testdir/test_source.vim b/src/nvim/testdir/test_source.vim
index 09baec0b7d..ba6fd5ad95 100644
--- a/src/nvim/testdir/test_source.vim
+++ b/src/nvim/testdir/test_source.vim
@@ -46,3 +46,45 @@ func Test_source_sandbox()
bwipe!
call delete('Xsourcehello')
endfunc
+
+" When deleting a file and immediately creating a new one the inode may be
+" recycled. Vim should not recognize it as the same script.
+func Test_different_script()
+ call writefile(['let s:var = "asdf"'], 'XoneScript')
+ source XoneScript
+ call delete('XoneScript')
+ call writefile(['let g:var = s:var'], 'XtwoScript')
+ call assert_fails('source XtwoScript', 'E121:')
+ call delete('XtwoScript')
+endfunc
+
+" When sourcing a vim script, shebang should be ignored.
+func Test_source_ignore_shebang()
+ call writefile(['#!./xyzabc', 'let g:val=369'], 'Xfile.vim')
+ source Xfile.vim
+ call assert_equal(g:val, 369)
+ call delete('Xfile.vim')
+endfunc
+
+" Test for expanding <sfile> in a autocmd and for <slnum> and <sflnum>
+func Test_source_autocmd_sfile()
+ let code =<< trim [CODE]
+ let g:SfileName = ''
+ augroup sfiletest
+ au!
+ autocmd User UserAutoCmd let g:Sfile = '<sfile>:t'
+ augroup END
+ doautocmd User UserAutoCmd
+ let g:Slnum = expand('<slnum>')
+ let g:Sflnum = expand('<sflnum>')
+ augroup! sfiletest
+ [CODE]
+ call writefile(code, 'Xscript.vim')
+ source Xscript.vim
+ call assert_equal('Xscript.vim', g:Sfile)
+ call assert_equal('7', g:Slnum)
+ call assert_equal('8', g:Sflnum)
+ call delete('Xscript.vim')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index 215d4387d6..7744c5bcca 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -2,9 +2,7 @@
" Note: this file uses latin1 encoding, but is used with utf-8 encoding.
source check.vim
-if !has('spell')
- finish
-endif
+CheckFeature spell
source screendump.vim
@@ -16,6 +14,8 @@ func TearDown()
call delete('Xtest.latin1.add.spl')
call delete('Xtest.latin1.spl')
call delete('Xtest.latin1.sug')
+ " set 'encoding' to clear the word list
+ set encoding=utf-8
endfunc
func Test_wrap_search()
@@ -131,6 +131,106 @@ foobar/?
set spell&
endfunc
+func Test_spell_file_missing()
+ let s:spell_file_missing = 0
+ augroup TestSpellFileMissing
+ autocmd! SpellFileMissing * let s:spell_file_missing += 1
+ augroup END
+
+ set spell spelllang=ab_cd
+ let messages = GetMessages()
+ " This message is not shown in Nvim because of #3027
+ " call assert_equal('Warning: Cannot find word list "ab.utf-8.spl" or "ab.ascii.spl"', messages[-1])
+ call assert_equal(1, s:spell_file_missing)
+
+ new XTestSpellFileMissing
+ augroup TestSpellFileMissing
+ autocmd! SpellFileMissing * bwipe
+ augroup END
+ call assert_fails('set spell spelllang=ab_cd', 'E797:')
+
+ " clean up
+ augroup TestSpellFileMissing
+ autocmd! SpellFileMissing
+ augroup END
+ augroup! TestSpellFileMissing
+ unlet s:spell_file_missing
+ set spell& spelllang&
+ %bwipe!
+endfunc
+
+func Test_spelldump()
+ " In case the spell file is not found avoid getting the download dialog, we
+ " would get stuck at the prompt.
+ let g:en_not_found = 0
+ augroup TestSpellFileMissing
+ au! SpellFileMissing * let g:en_not_found = 1
+ augroup END
+ set spell spelllang=en
+ spellrare! emacs
+ if g:en_not_found
+ call assert_report("Could not find English spell file")
+ else
+ spelldump
+
+ " Check assumption about region: 1: us, 2: au, 3: ca, 4: gb, 5: nz.
+ call assert_equal('/regions=usaucagbnz', getline(1))
+ call assert_notequal(0, search('^theater/1$')) " US English only.
+ call assert_notequal(0, search('^theatre/2345$')) " AU, CA, GB or NZ English.
+
+ call assert_notequal(0, search('^emacs/?$')) " ? for a rare word.
+ call assert_notequal(0, search('^the the/!$')) " ! for a wrong word.
+ endif
+
+ " clean up
+ unlet g:en_not_found
+ augroup TestSpellFileMissing
+ autocmd! SpellFileMissing
+ augroup END
+ augroup! TestSpellFileMissing
+ bwipe
+ set spell&
+endfunc
+
+func Test_spelldump_bang()
+ new
+ call setline(1, 'This is a sample sentence.')
+ redraw
+
+ " In case the spell file is not found avoid getting the download dialog, we
+ " would get stuck at the prompt.
+ let g:en_not_found = 0
+ augroup TestSpellFileMissing
+ au! SpellFileMissing * let g:en_not_found = 1
+ augroup END
+
+ set spell
+
+ if g:en_not_found
+ call assert_report("Could not find English spell file")
+ else
+ redraw
+ spelldump!
+
+ " :spelldump! includes the number of times a word was found while updating
+ " the screen.
+ " Common word count starts at 10, regular word count starts at 0.
+ call assert_notequal(0, search("^is\t11$")) " common word found once.
+ call assert_notequal(0, search("^the\t10$")) " common word never found.
+ call assert_notequal(0, search("^sample\t1$")) " regular word found once.
+ call assert_equal(0, search("^screen\t")) " regular word never found.
+ endif
+
+ " clean up
+ unlet g:en_not_found
+ augroup TestSpellFileMissing
+ autocmd! SpellFileMissing
+ augroup END
+ augroup! TestSpellFileMissing
+ %bwipe!
+ set spell&
+endfunc
+
func Test_spelllang_inv_region()
set spell spelllang=en_xx
let messages = GetMessages()
@@ -185,6 +285,18 @@ func Test_spellreall()
bwipe!
endfunc
+func Test_spell_dump_word_length()
+ " this was running over MAXWLEN
+ new
+ noremap 0 0a0zW0000000
+ sil! norm 0z=0
+ sil norm 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ sil! norm 0z=0
+
+ bwipe!
+ nunmap 0
+endfunc
+
" Test spellsuggest({word} [, {max} [, {capital}]])
func Test_spellsuggest()
" Verify suggestions are given even when spell checking is not enabled.
@@ -626,6 +738,10 @@ func Test_zz_sal_and_addition()
set spl=Xtest_ca.latin1.spl
call assert_equal("elequint", FirstSpellWord())
call assert_equal("elekwint", SecondSpellWord())
+
+ bwipe!
+ set spellfile=
+ set spl&
endfunc
func Test_spellfile_value()
@@ -699,6 +815,28 @@ func Test_spellsuggest_too_deep()
bwipe!
endfunc
+func Test_spell_good_word_invalid()
+ " This was adding a word with a 0x02 byte, which causes havoc.
+ enew
+ norm o0
+ sil! norm rzzWs00/
+ 2
+ sil! norm VzGprzzW
+ sil! norm z=
+
+ bwipe!
+endfunc
+
+func Test_spell_good_word_slash()
+ " This caused E1280.
+ new
+ norm afoo /
+ 1
+ norm zG
+
+ bwipe!
+endfunc
+
func LoadAffAndDic(aff_contents, dic_contents)
throw 'skipped: Nvim does not support enc=latin1'
set enc=latin1
diff --git a/src/nvim/testdir/test_spell_utf8.vim b/src/nvim/testdir/test_spell_utf8.vim
index 3c07e0782b..b7e3da37cb 100644
--- a/src/nvim/testdir/test_spell_utf8.vim
+++ b/src/nvim/testdir/test_spell_utf8.vim
@@ -13,6 +13,8 @@ func TearDown()
call delete('Xtest.utf-8.add.spl')
call delete('Xtest.utf-8.spl')
call delete('Xtest.utf-8.sug')
+ " set 'encoding' to clear the word list
+ set encoding=utf-8
endfunc
let g:test_data_aff1 = [
@@ -484,7 +486,6 @@ let g:test_data_aff_sal = [
\ ]
func LoadAffAndDic(aff_contents, dic_contents)
- set enc=utf-8
set spellfile=
call writefile(a:aff_contents, "Xtest.aff")
call writefile(a:dic_contents, "Xtest.dic")
@@ -760,6 +761,7 @@ func Test_spell_sal_and_addition()
call assert_equal("elequint", FirstSpellWord())
call assert_equal("elekwint", SecondSpellWord())
+ bwipe!
set spellfile=
set spl&
endfunc
@@ -780,7 +782,12 @@ func Test_no_crash_with_weird_text()
€
END
call setline(1, lines)
- exe "%norm \<C-v>ez=>\<C-v>wzG"
+ try
+ exe "%norm \<C-v>ez=>\<C-v>wzG"
+ catch /E1280:/
+ let caught = 'yes'
+ endtry
+ call assert_equal('yes', caught)
bwipe!
endfunc
@@ -798,10 +805,20 @@ func Test_word_index()
sil norm z=
bwipe!
- " clear the word list
- set enc=utf-8
call delete('Xtmpfile')
endfunc
+func Test_check_empty_line()
+ " This was using freed memory
+ enew
+ spellgood! fl
+ norm z=
+ norm yy
+ sil! norm P]svc
+ norm P]s
+
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_spellfile.vim b/src/nvim/testdir/test_spellfile.vim
index 0f48ab8f6f..b028e9d969 100644
--- a/src/nvim/testdir/test_spellfile.vim
+++ b/src/nvim/testdir/test_spellfile.vim
@@ -167,8 +167,632 @@ func Test_spell_normal()
call assert_equal([], glob('Xspellfile.add',0,1))
call assert_equal([], glob('Xspellfile2.add',0,1))
+ set spellfile= spell& spelllang&
+ bw!
+endfunc
+
+" Spell file content test. Write 'content' to the spell file prefixed by the
+" spell file header and then enable spell checking. If 'emsg' is not empty,
+" then check for error.
+func Spellfile_Test(content, emsg)
+ let splfile = './Xtest/spell/Xtest.utf-8.spl'
+ " Add the spell file header and version (VIMspell2)
+ let v = 0z56494D7370656C6C32 + a:content
+ call writefile(v, splfile, 'b')
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ if a:emsg != ''
+ call assert_fails('set spell', a:emsg)
+ else
+ " FIXME: With some invalid spellfile contents, there are no error
+ " messages. So don't know how to check for the test result.
+ set spell
+ endif
+ set nospell spelllang& rtp&
+endfunc
+
+" Test for spell file format errors.
+" The spell file format is described in spellfile.c
+func Test_spellfile_format_error()
+ let save_rtp = &rtp
+ call mkdir('Xtest/spell', 'p')
+ let splfile = './Xtest/spell/Xtest.utf-8.spl'
+
+ " empty spell file
+ call writefile([], splfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ call assert_fails('set spell', 'E757:')
+ set nospell spelllang&
+
+ " invalid file ID
+ call writefile(0z56494D, splfile, 'b')
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ call assert_fails('set spell', 'E757:')
+ set nospell spelllang&
+
+ " missing version number
+ call writefile(0z56494D7370656C6C, splfile, 'b')
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ call assert_fails('set spell', 'E771:')
+ set nospell spelllang&
+
+ " invalid version number
+ call writefile(0z56494D7370656C6C7A, splfile, 'b')
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ call assert_fails('set spell', 'E772:')
+ set nospell spelllang&
+
+ " no sections
+ call Spellfile_Test(0z, 'E758:')
+
+ " missing section length
+ call Spellfile_Test(0z00, 'E758:')
+
+ " unsupported required section
+ call Spellfile_Test(0z7A0100000004, 'E770:')
+
+ " unsupported not-required section
+ call Spellfile_Test(0z7A0000000004, 'E758:')
+
+ " SN_REGION: invalid number of region names
+ call Spellfile_Test(0z0000000000FF, 'E759:')
+
+ " SN_CHARFLAGS: missing <charflagslen> length
+ call Spellfile_Test(0z010000000004, 'E758:')
+
+ " SN_CHARFLAGS: invalid <charflagslen> length
+ call Spellfile_Test(0z0100000000010201, '')
+
+ " SN_CHARFLAGS: charflagslen == 0 and folcharslen != 0
+ call Spellfile_Test(0z01000000000400000101, 'E759:')
+
+ " SN_CHARFLAGS: missing <folcharslen> length
+ call Spellfile_Test(0z01000000000100, 'E758:')
+
+ " SN_PREFCOND: invalid prefcondcnt
+ call Spellfile_Test(0z03000000000100, 'E759:')
+
+ " SN_PREFCOND: invalid condlen
+ call Spellfile_Test(0z0300000000020001, 'E759:')
+
+ " SN_REP: invalid repcount
+ call Spellfile_Test(0z04000000000100, 'E758:')
+
+ " SN_REP: missing rep
+ call Spellfile_Test(0z0400000000020004, 'E758:')
+
+ " SN_REP: zero repfromlen
+ call Spellfile_Test(0z040000000003000100, 'E759:')
+
+ " SN_REP: invalid reptolen
+ call Spellfile_Test(0z0400000000050001014101, '')
+
+ " SN_REP: zero reptolen
+ call Spellfile_Test(0z0400000000050001014100, 'E759:')
+
+ " SN_SAL: missing salcount
+ call Spellfile_Test(0z05000000000102, 'E758:')
+
+ " SN_SAL: missing salfromlen
+ call Spellfile_Test(0z050000000003080001, 'E758:')
+
+ " SN_SAL: missing saltolen
+ call Spellfile_Test(0z0500000000050400010161, 'E758:')
+
+ " SN_WORDS: non-NUL terminated word
+ call Spellfile_Test(0z0D000000000376696D, 'E758:')
+
+ " SN_WORDS: very long word
+ let v = eval('0z0D000000012C' .. repeat('41', 300))
+ call Spellfile_Test(v, 'E759:')
+
+ " SN_SOFO: missing sofofromlen
+ call Spellfile_Test(0z06000000000100, 'E758:')
+
+ " SN_SOFO: missing sofotolen
+ call Spellfile_Test(0z06000000000400016100, 'E758:')
+
+ " SN_SOFO: missing sofoto
+ call Spellfile_Test(0z0600000000050001610000, 'E759:')
+
+ " SN_COMPOUND: compmax is less than 2
+ call Spellfile_Test(0z08000000000101, 'E759:')
+
+ " SN_COMPOUND: missing compsylmax and other options
+ call Spellfile_Test(0z0800000000020401, 'E759:')
+
+ " SN_COMPOUND: missing compoptions
+ call Spellfile_Test(0z080000000005040101, 'E758:')
+
+ " SN_INFO: missing info
+ call Spellfile_Test(0z0F0000000005040101, '')
+
+ " SN_MIDWORD: missing midword
+ call Spellfile_Test(0z0200000000040102, '')
+
+ " SN_MAP: missing midword
+ call Spellfile_Test(0z0700000000040102, '')
+
+ " SN_SYLLABLE: missing SYLLABLE item
+ call Spellfile_Test(0z0900000000040102, '')
+
+ " SN_SYLLABLE: More than SY_MAXLEN size
+ let v = eval('0z090000000022612F' .. repeat('62', 32))
+ call Spellfile_Test(v, '')
+
+ " LWORDTREE: missing
+ call Spellfile_Test(0zFF, 'E758:')
+
+ " LWORDTREE: missing tree node
+ call Spellfile_Test(0zFF00000004, 'E758:')
+
+ " LWORDTREE: missing tree node value
+ call Spellfile_Test(0zFF0000000402, 'E758:')
+
+ " KWORDTREE: missing tree node
+ call Spellfile_Test(0zFF0000000000000004, 'E758:')
+
+ " PREFIXTREE: missing tree node
+ call Spellfile_Test(0zFF000000000000000000000004, 'E758:')
+
+ let &rtp = save_rtp
+ call delete('Xtest', 'rf')
+endfunc
+
+" Test for format errors in suggest file
+func Test_sugfile_format_error()
+ let save_rtp = &rtp
+ call mkdir('Xtest/spell', 'p')
+ let splfile = './Xtest/spell/Xtest.utf-8.spl'
+ let sugfile = './Xtest/spell/Xtest.utf-8.sug'
+
+ " create an empty spell file with a suggest timestamp
+ call writefile(0z56494D7370656C6C320B00000000080000000000000044FF000000000000000000000000, splfile, 'b')
+
+ " 'encoding' is set before each test to clear the previously loaded suggest
+ " file from memory.
+
+ " empty suggest file
+ set encoding=utf-8
+ call writefile([], sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E778:')
+ set nospell spelllang&
+
+ " zero suggest version
+ set encoding=utf-8
+ call writefile(0z56494D73756700, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E779:')
+ set nospell spelllang&
+
+ " unsupported suggest version
+ set encoding=utf-8
+ call writefile(0z56494D7375671F, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E780:')
+ set nospell spelllang&
+
+ " missing suggest timestamp
+ set encoding=utf-8
+ call writefile(0z56494D73756701, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E781:')
+ set nospell spelllang&
+
+ " incorrect suggest timestamp
+ set encoding=utf-8
+ call writefile(0z56494D7375670100000000000000FF, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E781:')
+ set nospell spelllang&
+
+ " missing suggest wordtree
+ set encoding=utf-8
+ call writefile(0z56494D737567010000000000000044, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E782:')
+ set nospell spelllang&
+
+ " invalid suggest word count in SUGTABLE
+ set encoding=utf-8
+ call writefile(0z56494D7375670100000000000000440000000022, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E782:')
+ set nospell spelllang&
+
+ " missing sugline in SUGTABLE
+ set encoding=utf-8
+ call writefile(0z56494D7375670100000000000000440000000000000005, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E782:')
+ set nospell spelllang&
+
+ let &rtp = save_rtp
+ call delete('Xtest', 'rf')
+endfunc
+
+" Test for using :mkspell to create a spell file from a list of words
+func Test_wordlist_dic()
+ " duplicate encoding
+ let lines =<< trim [END]
+ # This is an example word list
+
+ /encoding=latin1
+ /encoding=latin1
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell Xwordlist.spl Xwordlist.dic')
+ call assert_match('Duplicate /encoding= line ignored in Xwordlist.dic line 4: /encoding=latin1', output)
+
+ " multiple encoding for a word
+ let lines =<< trim [END]
+ example
+ /encoding=latin1
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('/encoding= line after word ignored in Xwordlist.dic line 2: /encoding=latin1', output)
+
+ " unsupported encoding for a word
+ let lines =<< trim [END]
+ /encoding=Xtest
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('Conversion in Xwordlist.dic not supported: from Xtest to utf-8', output)
+
+ " duplicate region
+ let lines =<< trim [END]
+ /regions=usca
+ /regions=usca
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('Duplicate /regions= line ignored in Xwordlist.dic line 2: regions=usca', output)
+
+ " maximum regions
+ let lines =<< trim [END]
+ /regions=uscauscauscauscausca
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('Too many regions in Xwordlist.dic line 1: uscauscauscauscausca', output)
+
+ " unsupported '/' value
+ let lines =<< trim [END]
+ /test=abc
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('/ line ignored in Xwordlist.dic line 1: /test=abc', output)
+
+ " unsupported flag
+ let lines =<< trim [END]
+ example/+
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('Unrecognized flags in Xwordlist.dic line 1: +', output)
+
+ " non-ascii word
+ call writefile(["ʀʀ"], 'Xwordlist.dic')
+ let output = execute('mkspell! -ascii Xwordlist.spl Xwordlist.dic')
+ call assert_match('Ignored 1 words with non-ASCII characters', output)
+
+ call delete('Xwordlist.spl')
+ call delete('Xwordlist.dic')
+endfunc
+
+" Test for the :mkspell command
+func Test_mkspell()
+ call assert_fails('mkspell Xtest_us.spl', 'E751:')
+ call assert_fails('mkspell a b c d e f g h i j k', 'E754:')
+
+ call writefile([], 'Xtest.spl')
+ call writefile([], 'Xtest.dic')
+ call assert_fails('mkspell Xtest.spl Xtest.dic', 'E13:')
+ call delete('Xtest.spl')
+ call delete('Xtest.dic')
+
+ call mkdir('Xtest.spl')
+ call assert_fails('mkspell! Xtest.spl Xtest.dic', 'E17:')
+ call delete('Xtest.spl', 'rf')
+
+ " can't write the .spl file as its directory does not exist
+ call writefile([], 'Xtest.aff')
+ call writefile([], 'Xtest.dic')
+ call assert_fails('mkspell DOES_NOT_EXIT/Xtest.spl Xtest.dic', 'E484:')
+ call delete('Xtest.aff')
+ call delete('Xtest.dic')
+
+ call assert_fails('mkspell en en_US abc_xyz', 'E755:')
+endfunc
+
+" Tests for :mkspell with a .dic and .aff file
+func Test_aff_file_format_error()
+ " FIXME: For some reason, the :mkspell command below doesn't fail on the
+ " MS-Windows CI build. Disable this test on MS-Windows for now.
+ CheckNotMSWindows
+
+ " No word count in .dic file
+ call writefile([], 'Xtest.dic')
+ call writefile([], 'Xtest.aff')
+ call assert_fails('mkspell! Xtest.spl Xtest', 'E760:')
+
+ " create a .dic file for the tests below
+ call writefile(['1', 'work'], 'Xtest.dic')
+
+ " Invalid encoding in .aff file
+ call writefile(['# comment', 'SET Xinvalidencoding'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Conversion in Xtest.aff not supported: from xinvalidencoding', output)
+
+ " Invalid flag in .aff file
+ call writefile(['FLAG xxx'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Invalid value for FLAG in Xtest.aff line 1: xxx', output)
+
+ " set FLAGS after using flag for an affix
+ call writefile(['SFX L Y 1', 'SFX L 0 re [^x]', 'FLAG long'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('FLAG after using flags in Xtest.aff line 3: long', output)
+
+ " INFO in affix file
+ let save_encoding = &encoding
+ call mkdir('Xrtp/spell', 'p')
+ call writefile(['1', 'work'], 'Xrtp/spell/Xtest.dic')
+ call writefile(['NAME klingon', 'VERSION 1.4', 'AUTHOR Spock'],
+ \ 'Xrtp/spell/Xtest.aff')
+ silent mkspell! Xrtp/spell/Xtest.utf-8.spl Xrtp/spell/Xtest
+ let save_rtp = &rtp
+ set runtimepath=./Xrtp
+ set spelllang=Xtest
+ set spell
+ let output = split(execute('spellinfo'), "\n")
+ call assert_equal("NAME klingon", output[1])
+ call assert_equal("VERSION 1.4", output[2])
+ call assert_equal("AUTHOR Spock", output[3])
+ let &rtp = save_rtp
+ call delete('Xrtp', 'rf')
+ set spell& spelllang& spellfile&
+ %bw!
+ " 'encoding' must be set again to clear the spell file in memory
+ let &encoding = save_encoding
+
+ " COMPOUNDFORBIDFLAG flag after PFX in an affix file
+ call writefile(['PFX L Y 1', 'PFX L 0 re x', 'COMPOUNDFLAG c', 'COMPOUNDFORBIDFLAG x'],
+ \ 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in Xtest.aff line 4', output)
+
+ " COMPOUNDPERMITFLAG flag after PFX in an affix file
+ call writefile(['PFX L Y 1', 'PFX L 0 re x', 'COMPOUNDPERMITFLAG c'],
+ \ 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in Xtest.aff line 3', output)
+
+ " Wrong COMPOUNDRULES flag value in an affix file
+ call writefile(['COMPOUNDRULES a'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Wrong COMPOUNDRULES value in Xtest.aff line 1: a', output)
+
+ " Wrong COMPOUNDWORDMAX flag value in an affix file
+ call writefile(['COMPOUNDWORDMAX 0'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Wrong COMPOUNDWORDMAX value in Xtest.aff line 1: 0', output)
+
+ " Wrong COMPOUNDMIN flag value in an affix file
+ call writefile(['COMPOUNDMIN 0'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Wrong COMPOUNDMIN value in Xtest.aff line 1: 0', output)
+
+ " Wrong COMPOUNDSYLMAX flag value in an affix file
+ call writefile(['COMPOUNDSYLMAX 0'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Wrong COMPOUNDSYLMAX value in Xtest.aff line 1: 0', output)
+
+ " Wrong CHECKCOMPOUNDPATTERN flag value in an affix file
+ call writefile(['CHECKCOMPOUNDPATTERN 0'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Wrong CHECKCOMPOUNDPATTERN value in Xtest.aff line 1: 0', output)
+
+ " Both compounding and NOBREAK specified
+ call writefile(['COMPOUNDFLAG c', 'NOBREAK'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Warning: both compounding and NOBREAK specified', output)
+
+ " Duplicate affix entry in an affix file
+ call writefile(['PFX L Y 1', 'PFX L 0 re x', 'PFX L Y 1', 'PFX L 0 re x'],
+ \ 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Duplicate affix in Xtest.aff line 3: L', output)
+
+ " Duplicate affix entry in an affix file
+ call writefile(['PFX L Y 1', 'PFX L Y 1'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Unrecognized or duplicate item in Xtest.aff line 2: PFX', output)
+
+ " Different combining flags in an affix file
+ call writefile(['PFX L Y 1', 'PFX L 0 re x', 'PFX L N 1'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Different combining flag in continued affix block in Xtest.aff line 3', output)
+
+ " Try to reuse a affix used for BAD flag
+ call writefile(['BAD x', 'PFX x Y 1', 'PFX x 0 re x'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in Xtest.aff line 2: x', output)
+
+ " Trailing characters in an affix entry
+ call writefile(['PFX L Y 1 Test', 'PFX L 0 re x'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Trailing text in Xtest.aff line 1: Test', output)
+
+ " Trailing characters in an affix entry
+ call writefile(['PFX L Y 1', 'PFX L 0 re x Test'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Trailing text in Xtest.aff line 2: Test', output)
+
+ " Incorrect combine flag in an affix entry
+ call writefile(['PFX L X 1', 'PFX L 0 re x'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Expected Y or N in Xtest.aff line 1: X', output)
+
+ " Invalid count for REP item
+ call writefile(['REP a'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Expected REP(SAL) count in Xtest.aff line 1', output)
+
+ " Trailing characters in REP item
+ call writefile(['REP 1', 'REP f ph test'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Trailing text in Xtest.aff line 2: test', output)
+
+ " Invalid count for MAP item
+ call writefile(['MAP a'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Expected MAP count in Xtest.aff line 1', output)
+
+ " Duplicate character in a MAP item
+ call writefile(['MAP 2', 'MAP xx', 'MAP yy'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Duplicate character in MAP in Xtest.aff line 2', output)
+
+ " Use COMPOUNDSYLMAX without SYLLABLE
+ call writefile(['COMPOUNDSYLMAX 2'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('COMPOUNDSYLMAX used without SYLLABLE', output)
+
+ " Missing SOFOTO
+ call writefile(['SOFOFROM abcdef'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Missing SOFOTO line in Xtest.aff', output)
+
+ " Length of SOFOFROM and SOFOTO differ
+ call writefile(['SOFOFROM abcde', 'SOFOTO ABCD'], 'Xtest.aff')
+ call assert_fails('mkspell! Xtest.spl Xtest', 'E759:')
+
+ " Both SAL and SOFOFROM/SOFOTO items
+ call writefile(['SOFOFROM abcd', 'SOFOTO ABCD', 'SAL CIA X'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Both SAL and SOFO lines in Xtest.aff', output)
+
+ " use an alphabet flag when FLAG is num
+ call writefile(['FLAG num', 'SFX L Y 1', 'SFX L 0 re [^x]'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Flag is not a number in Xtest.aff line 2: L', output)
+
+ " use number and alphabet flag when FLAG is num
+ call writefile(['FLAG num', 'SFX 4f Y 1', 'SFX 4f 0 re [^x]'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Affix name too long in Xtest.aff line 2: 4f', output)
+
+ " use a single character flag when FLAG is long
+ call writefile(['FLAG long', 'SFX L Y 1', 'SFX L 0 re [^x]'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Illegal flag in Xtest.aff line 2: L', output)
+
+ " duplicate word in the .dic file
+ call writefile(['2', 'good', 'good', 'good'], 'Xtest.dic')
+ call writefile(['NAME vim'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('First duplicate word in Xtest.dic line 3: good', output)
+ call assert_match('2 duplicate word(s) in Xtest.dic', output)
+
+ call delete('Xtest.dic')
+ call delete('Xtest.aff')
+ call delete('Xtest.spl')
+ call delete('Xtest.sug')
+endfunc
+
+func Test_spell_add_word()
set spellfile=
+ call assert_fails('spellgood abc', 'E764:')
+
+ set spellfile=Xtest.utf-8.add
+ call assert_fails('2spellgood abc', 'E765:')
+
+ edit Xtest.utf-8.add
+ call setline(1, 'sample')
+ call assert_fails('spellgood abc', 'E139:')
+ set spellfile&
+ %bw!
+endfunc
+
+func Test_spellfile_verbose()
+ call writefile(['1', 'one'], 'XtestVerbose.dic')
+ call writefile([], 'XtestVerbose.aff')
+ mkspell! XtestVerbose-utf8.spl XtestVerbose
+ set spell
+
+ " First time: the spl file should be read.
+ let a = execute('3verbose set spelllang=XtestVerbose-utf8.spl')
+ call assert_match('Reading spell file "XtestVerbose-utf8.spl"', a)
+
+ " Second time time: the spl file should not be read (already read).
+ let a = execute('3verbose set spelllang=XtestVerbose-utf8.spl')
+ call assert_notmatch('Reading spell file "XtestVerbose-utf8.spl"', a)
+
+ set spell& spelllang&
+ call delete('XtestVerbose.dic')
+ call delete('XtestVerbose.aff')
+ call delete('XtestVerbose-utf8.spl')
+endfunc
+
+" Test NOBREAK (see :help spell-NOBREAK)
+func Test_NOBREAK()
+ call writefile(['3', 'one', 'two', 'three' ], 'XtestNOBREAK.dic')
+ call writefile(['NOBREAK' ], 'XtestNOBREAK.aff')
+
+ mkspell! XtestNOBREAK-utf8.spl XtestNOBREAK
+ set spell spelllang=XtestNOBREAK-utf8.spl
+
+ call assert_equal(['', ''], spellbadword('One two three onetwo onetwothree threetwoone'))
+
+ call assert_equal(['x', 'bad'], spellbadword('x'))
+ call assert_equal(['y', 'bad'], spellbadword('yone'))
+ call assert_equal(['z', 'bad'], spellbadword('onez'))
+ call assert_equal(['zero', 'bad'], spellbadword('Onetwozerothree'))
+
+ new
+ call setline(1, 'Onetwwothree')
+ norm! fw1z=
+ call assert_equal('Onetwothree', getline(1))
+ call setline(1, 'Onetwothre')
+ norm! fh1z=
+ call assert_equal('Onetwothree', getline(1))
+
bw!
+ set spell& spelllang&
+ call delete('XtestNOBREAK.dic')
+ call delete('XtestNOBREAK.aff')
+ call delete('XtestNOBREAK-utf8.spl')
endfunc
" Test CHECKCOMPOUNDPATTERN (see :help spell-CHECKCOMPOUNDPATTERN)
@@ -183,7 +807,7 @@ func Test_spellfile_CHECKCOMPOUNDPATTERN()
\ 'CHECKCOMPOUNDPATTERN wo on',
\ 'COMPOUNDFLAG c'], 'XtestCHECKCOMPOUNDPATTERN.aff')
- let output = execute('mkspell! XtestCHECKCOMPOUNDPATTERN-utf8.spl XtestCHECKCOMPOUNDPATTERN')
+ mkspell! XtestCHECKCOMPOUNDPATTERN-utf8.spl XtestCHECKCOMPOUNDPATTERN
set spell spelllang=XtestCHECKCOMPOUNDPATTERN-utf8.spl
" Check valid words with and without valid compounds.
@@ -268,7 +892,7 @@ func Test_spellfile_COMMON()
\ 'ted'], 'XtestCOMMON.dic')
call writefile(['COMMON the and'], 'XtestCOMMON.aff')
- let output = execute('mkspell! XtestCOMMON-utf8.spl XtestCOMMON')
+ mkspell! XtestCOMMON-utf8.spl XtestCOMMON
set spell spelllang=XtestCOMMON-utf8.spl
" COMMON words 'and' and 'the' should be the top suggestions.
@@ -283,4 +907,121 @@ func Test_spellfile_COMMON()
call delete('XtestCOMMON-utf8.spl')
endfunc
+" Test CIRCUMFIX (see: :help spell-CIRCUMFIX)
+func Test_spellfile_CIRCUMFIX()
+ " Example taken verbatim from https://github.com/hunspell/hunspell/tree/master/tests
+ call writefile(['1',
+ \ 'nagy/C po:adj'], 'XtestCIRCUMFIX.dic')
+ call writefile(['# circumfixes: ~ obligate prefix/suffix combinations',
+ \ '# superlative in Hungarian: leg- (prefix) AND -bb (suffix)',
+ \ '',
+ \ 'CIRCUMFIX X',
+ \ '',
+ \ 'PFX A Y 1',
+ \ 'PFX A 0 leg/X .',
+ \ '',
+ \ 'PFX B Y 1',
+ \ 'PFX B 0 legesleg/X .',
+ \ '',
+ \ 'SFX C Y 3',
+ \ 'SFX C 0 obb . is:COMPARATIVE',
+ \ 'SFX C 0 obb/AX . is:SUPERLATIVE',
+ \ 'SFX C 0 obb/BX . is:SUPERSUPERLATIVE'], 'XtestCIRCUMFIX.aff')
+
+ mkspell! XtestCIRCUMFIX-utf8.spl XtestCIRCUMFIX
+ set spell spelllang=XtestCIRCUMFIX-utf8.spl
+
+ " From https://catalog.ldc.upenn.edu/docs/LDC2008T01/acta04.pdf:
+ " Hungarian English
+ " --------- -------
+ " nagy great
+ " nagyobb greater
+ " legnagyobb greatest
+ " legeslegnagyob most greatest
+ call assert_equal(['', ''], spellbadword('nagy nagyobb legnagyobb legeslegnagyobb'))
+
+ for badword in ['legnagy', 'legeslegnagy', 'legobb', 'legeslegobb']
+ call assert_equal([badword, 'bad'], spellbadword(badword))
+ endfor
+
+ set spell& spelllang&
+ call delete('XtestCIRCUMFIX.dic')
+ call delete('XtestCIRCUMFIX.aff')
+ call delete('XtestCIRCUMFIX-utf8.spl')
+endfunc
+
+" Test SFX that strips/chops characters
+func Test_spellfile_SFX_strip()
+ " Simplified conjugation of Italian verbs ending in -are (first conjugation).
+ call writefile(['SFX A Y 4',
+ \ 'SFX A are iamo [^icg]are',
+ \ 'SFX A are hiamo [cg]are',
+ \ 'SFX A re mo iare',
+ \ 'SFX A re vamo are'],
+ \ 'XtestSFX.aff')
+ " Examples of Italian verbs:
+ " - cantare = to sing
+ " - cercare = to search
+ " - odiare = to hate
+ call writefile(['3', 'cantare/A', 'cercare/A', 'odiare/A'], 'XtestSFX.dic')
+
+ mkspell! XtestSFX-utf8.spl XtestSFX
+ set spell spelllang=XtestSFX-utf8.spl
+
+ " To sing, we're singing, we were singing.
+ call assert_equal(['', ''], spellbadword('cantare cantiamo cantavamo'))
+
+ " To search, we're searching, we were searching.
+ call assert_equal(['', ''], spellbadword('cercare cerchiamo cercavamo'))
+
+ " To hate, we hate, we were hating.
+ call assert_equal(['', ''], spellbadword('odiare odiamo odiavamo'))
+
+ for badword in ['canthiamo', 'cerciamo', 'cantarevamo', 'odiiamo']
+ call assert_equal([badword, 'bad'], spellbadword(badword))
+ endfor
+
+ call assert_equal(['cantiamo'], spellsuggest('canthiamo', 1))
+ call assert_equal(['cerchiamo'], spellsuggest('cerciamo', 1))
+ call assert_equal(['cantavamo'], spellsuggest('cantarevamo', 1))
+ call assert_equal(['odiamo'], spellsuggest('odiiamo', 1))
+
+ set spell& spelllang&
+ call delete('XtestSFX.dic')
+ call delete('XtestSFX.aff')
+ call delete('XtestSFX-utf8.spl')
+endfunc
+
+" When 'spellfile' is not set, adding a new good word will automatically set
+" the 'spellfile'
+func Test_init_spellfile()
+ let save_rtp = &rtp
+ let save_encoding = &encoding
+ call mkdir('Xrtp/spell', 'p')
+ call writefile(['vim'], 'Xrtp/spell/Xtest.dic')
+ silent mkspell Xrtp/spell/Xtest.utf-8.spl Xrtp/spell/Xtest.dic
+ set runtimepath=./Xrtp
+ set spelllang=Xtest
+ set spell
+ silent spellgood abc
+ call assert_equal('./Xrtp/spell/Xtest.utf-8.add', &spellfile)
+ call assert_equal(['abc'], readfile('Xrtp/spell/Xtest.utf-8.add'))
+ call assert_true(filereadable('Xrtp/spell/Xtest.utf-8.spl'))
+ set spell& spelllang& spellfile&
+ call delete('Xrtp', 'rf')
+ let &encoding = save_encoding
+ let &rtp = save_rtp
+ %bw!
+endfunc
+
+" Test for the 'mkspellmem' option
+func Test_mkspellmem_opt()
+ call assert_fails('set mkspellmem=1000', 'E474:')
+ call assert_fails('set mkspellmem=1000,', 'E474:')
+ call assert_fails('set mkspellmem=1000,50', 'E474:')
+ call assert_fails('set mkspellmem=1000,50,', 'E474:')
+ call assert_fails('set mkspellmem=1000,50,10,', 'E474:')
+ call assert_fails('set mkspellmem=1000,50,0', 'E474:')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim
index 8579457d9f..ec35fac964 100644
--- a/src/nvim/testdir/test_statusline.vim
+++ b/src/nvim/testdir/test_statusline.vim
@@ -2,13 +2,15 @@
"
" Not tested yet:
" %N
-" %T
-" %X
source view_util.vim
source check.vim
source term_util.vim
+func SetUp()
+ set laststatus=2
+endfunc
+
func s:get_statusline()
return ScreenLines(&lines - 1, &columns)[0]
endfunc
@@ -104,6 +106,18 @@ func Test_statusline()
set statusline=%F
call assert_match('/testdir/Xstatusline\s*$', s:get_statusline())
+ " Test for min and max width with %(. For some reason, if this test is moved
+ " after the below test for the help buffer flag, then the code to truncate
+ " the string is not executed.
+ set statusline=%015(%f%)
+ call assert_match('^ Xstatusline\s*$', s:get_statusline())
+ set statusline=%.6(%f%)
+ call assert_match('^<sline\s*$', s:get_statusline())
+ set statusline=%14f
+ call assert_match('^ Xstatusline\s*$', s:get_statusline())
+ set statusline=%.4L
+ call assert_match('^10>3\s*$', s:get_statusline())
+
" %h: Help buffer flag, text is "[help]".
" %H: Help buffer flag, text is ",HLP".
set statusline=%h,%H
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index 8483435062..f795d1c0cf 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -1,5 +1,7 @@
" Tests for the substitute (:s) command
+source shared.vim
+
func Test_multiline_subst()
enew!
call append(0, ["1 aa",
@@ -140,7 +142,7 @@ func Test_substitute_repeat()
" This caused an invalid memory access.
split Xfile
s/^/x
- call feedkeys("gQsc\<CR>y", 'tx')
+ call feedkeys("Qsc\<CR>y", 'tx')
bwipe!
endfunc
@@ -292,7 +294,7 @@ endfunc
" Test for *:s%* on :substitute.
func Test_sub_cmd_6()
- throw "skipped: Nvim removed POSIX-related 'cpoptions' flags"
+ throw 'Skipped: Nvim does not support cpoptions flag "/"'
set magic&
set cpo+=/
@@ -806,6 +808,41 @@ func Test_sub_expand_text()
close!
endfunc
+" Test for command failures when the last substitute pattern is not set.
+func Test_sub_with_no_last_pat()
+ let lines =<< trim [SCRIPT]
+ call assert_fails('~', 'E33:')
+ call assert_fails('s//abc/g', 'E476:')
+ call assert_fails('s\/bar', 'E476:')
+ call assert_fails('s\&bar&', 'E476:')
+ call writefile(v:errors, 'Xresult')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+
+ " Nvim does not support cpoptions flag "/"'
+ " let lines =<< trim [SCRIPT]
+ " set cpo+=/
+ " call assert_fails('s/abc/%/', 'E33:')
+ " call writefile(v:errors, 'Xresult')
+ " qall!
+ " [SCRIPT]
+ " call writefile(lines, 'Xscript')
+ " if RunVim([], [], '--clean -S Xscript')
+ " call assert_equal([], readfile('Xresult'))
+ " endif
+
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+func Test_substitute()
+ call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
+endfunc
+
func Test_submatch_list_concatenate()
let pat = 'A\(.\)'
let Rep = {-> string([submatch(0, 1)] + [[submatch(1)]])}
@@ -851,6 +888,54 @@ func Test_sub_change_window()
delfunc Repl
endfunc
+" This was undoign a change in between computing the length and using it.
+func Do_Test_sub_undo_change()
+ new
+ norm o0000000000000000000000000000000000000000000000000000
+ silent! s/\%')/\=Repl()
+ bwipe!
+endfunc
+
+func Test_sub_undo_change()
+ func Repl()
+ silent! norm g-
+ endfunc
+ call Do_Test_sub_undo_change()
+
+ func! Repl()
+ silent earlier
+ endfunc
+ call Do_Test_sub_undo_change()
+
+ delfunc Repl
+endfunc
+
+" This was opening a command line window from the expression
+func Test_sub_open_cmdline_win()
+ " the error only happens in a very specific setup, run a new Vim instance to
+ " get a clean starting point.
+ let lines =<< trim [SCRIPT]
+ set vb t_vb=
+ norm o0000000000000000000000000000000000000000000000000000
+ func Replace()
+ norm q/
+ endfunc
+ s/\%')/\=Replace()
+ redir >Xresult
+ messages
+ redir END
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '-u NONE -S Xscript')
+ call assert_match('E565: Not allowed to change text or change window',
+ \ readfile('Xresult')->join('XX'))
+ endif
+
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
" Test for the 2-letter and 3-letter :substitute commands
func Test_substitute_short_cmd()
new
diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index b180f27685..923e1cbf50 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -278,7 +278,6 @@ func Test_swap_recover_ext()
autocmd SwapExists * let v:swapchoice = 'r'
augroup END
-
" Create a valid swapfile by editing a file with a special extension.
split Xtest.scr
call setline(1, ['one', 'two', 'three'])
@@ -311,6 +310,46 @@ func Test_swap_recover_ext()
augroup! test_swap_recover_ext
endfunc
+" Test for closing a split window automatically when a swap file is detected
+" and 'Q' is selected in the confirmation prompt.
+func Test_swap_split_win()
+ autocmd! SwapExists
+ augroup test_swap_splitwin
+ autocmd!
+ autocmd SwapExists * let v:swapchoice = 'q'
+ augroup END
+
+ " Create a valid swapfile by editing a file with a special extension.
+ split Xtest.scr
+ call setline(1, ['one', 'two', 'three'])
+ write " file is written, not modified
+ write " write again to make sure the swapfile is created
+ " read the swapfile as a Blob
+ let swapfile_name = swapname('%')
+ let swapfile_bytes = readfile(swapfile_name, 'B')
+
+ " Close and delete the file and recreate the swap file.
+ quit
+ call delete('Xtest.scr')
+ call writefile(swapfile_bytes, swapfile_name)
+ " Split edit the file again. This should fail to open the window
+ try
+ split Xtest.scr
+ catch
+ " E308 should be caught, not E306.
+ call assert_exception('E308:') " Original file may have been changed
+ endtry
+ call assert_equal(1, winnr('$'))
+
+ call delete('Xtest.scr')
+ call delete(swapfile_name)
+
+ augroup test_swap_splitwin
+ autocmd!
+ augroup END
+ augroup! test_swap_splitwin
+endfunc
+
" Test for selecting 'q' in the attention prompt
func Test_swap_prompt_splitwin()
CheckRunVimInTerminal
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 9f50b3c241..7ba0149971 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -1,5 +1,8 @@
" Test for syntax and syntax iskeyword option
+source check.vim
+CheckFeature syntax
+
source view_util.vim
source screendump.vim
@@ -197,6 +200,12 @@ func Test_syntax_completion()
call assert_match('^"syn match Boolean Character ', @:)
endfunc
+func Test_echohl_completion()
+ call feedkeys(":echohl no\<C-A>\<C-B>\"\<CR>", 'tx')
+ " call assert_equal('"echohl NonText Normal none', @:)
+ call assert_equal('"echohl NonText Normal NormalFloat NormalNC none', @:)
+endfunc
+
func Test_syntax_arg_skipped()
syn clear
syntax case ignore
diff --git a/src/nvim/testdir/test_tabline.vim b/src/nvim/testdir/test_tabline.vim
index 3a18206078..e58a412c5a 100644
--- a/src/nvim/testdir/test_tabline.vim
+++ b/src/nvim/testdir/test_tabline.vim
@@ -1,3 +1,4 @@
+" Test for tabline
source shared.vim
@@ -17,6 +18,9 @@ func TablineWithError()
endfunc
func Test_caught_error_in_tabline()
+ if has('gui')
+ set guioptions-=e
+ endif
let showtabline_save = &showtabline
set showtabline=2
let s:func_in_tabline_called = 0
@@ -30,6 +34,9 @@ func Test_caught_error_in_tabline()
endfunc
func Test_tabline_will_be_disabled_with_error()
+ if has('gui')
+ set guioptions-=e
+ endif
let showtabline_save = &showtabline
set showtabline=2
let s:func_in_tabline_called = 0
@@ -65,6 +72,47 @@ func Test_redrawtabline()
au! Bufadd
endfunc
+" Test for the "%T" and "%X" flags in the 'tabline' option
+func MyTabLine()
+ let s = ''
+ for i in range(tabpagenr('$'))
+ " set the tab page number (for mouse clicks)
+ let s .= '%' . (i + 1) . 'T'
+
+ " the label is made by MyTabLabel()
+ let s .= ' %{MyTabLabel(' . (i + 1) . ')} '
+ endfor
+
+ " after the last tab fill with TabLineFill and reset tab page nr
+ let s .= '%T'
+
+ " right-align the label to close the current tab page
+ if tabpagenr('$') > 1
+ let s .= '%=%Xclose'
+ endif
+
+ return s
+endfunc
+
+func MyTabLabel(n)
+ let buflist = tabpagebuflist(a:n)
+ let winnr = tabpagewinnr(a:n)
+ return bufname(buflist[winnr - 1])
+endfunc
+
+func Test_tabline_flags()
+ if has('gui')
+ set guioptions-=e
+ endif
+ set tabline=%!MyTabLine()
+ edit Xtabline1
+ tabnew Xtabline2
+ redrawtabline
+ call assert_match('^ Xtabline1 Xtabline2\s\+close$', Screenline(1))
+ set tabline=
+ %bw!
+endfunc
+
function EmptyTabname()
return ""
endfunction
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index 51ab5c1022..d891684ecb 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -139,7 +139,13 @@ function Test_tabpage()
call assert_fails("tabmove -99", 'E474:')
call assert_fails("tabmove -3+", 'E474:')
call assert_fails("tabmove $3", 'E474:')
+ call assert_fails("%tabonly", 'E16:')
1tabonly!
+ tabmove 1
+ call assert_equal(1, tabpagenr())
+ tabnew
+ call assert_fails("-2tabmove", 'E474:')
+ tabonly!
endfunc
" Test autocommands
@@ -607,6 +613,16 @@ func Test_tabpage_cmdheight()
call delete('XTest_tabpage_cmdheight')
endfunc
+" Test for closing the tab page from a command window
+func Test_tabpage_close_cmdwin()
+ tabnew
+ call feedkeys("q/:tabclose\<CR>\<Esc>", 'xt')
+ call assert_equal(2, tabpagenr('$'))
+ call feedkeys("q/:tabonly\<CR>\<Esc>", 'xt')
+ call assert_equal(2, tabpagenr('$'))
+ tabonly
+endfunc
+
" Return the terminal key code for selecting a tab page from the tabline. This
" sequence contains the following codes: a CSI (0x9b), KS_TABLINE (0xf0),
" KS_FILLER (0x58) and then the tab page number.
@@ -685,6 +701,69 @@ func Test_tabline_tabmenu()
%bw!
endfunc
+" Test for changing the current tab page from an autocmd when closing a tab
+" page.
+func Test_tabpage_switchtab_on_close()
+ only
+ tabnew
+ tabnew
+ " Test for BufLeave
+ augroup T1
+ au!
+ au BufLeave * tabfirst
+ augroup END
+ tabclose
+ call assert_equal(1, tabpagenr())
+ augroup T1
+ au!
+ augroup END
+
+ " Test for WinLeave
+ $tabnew
+ augroup T1
+ au!
+ au WinLeave * tabfirst
+ augroup END
+ tabclose
+ call assert_equal(1, tabpagenr())
+ augroup T1
+ au!
+ augroup END
+
+ " Test for TabLeave
+ $tabnew
+ augroup T1
+ au!
+ au TabLeave * tabfirst
+ augroup END
+ tabclose
+ call assert_equal(1, tabpagenr())
+ augroup T1
+ au!
+ augroup END
+ augroup! T1
+ tabonly
+endfunc
+
+" Test for closing the destination tabpage when jumping from one to another.
+func Test_tabpage_close_on_switch()
+ tabnew
+ tabnew
+ edit Xfile
+ augroup T2
+ au!
+ au BufLeave Xfile 1tabclose
+ augroup END
+ tabfirst
+ call assert_equal(2, tabpagenr())
+ call assert_equal('Xfile', @%)
+ augroup T2
+ au!
+ augroup END
+ augroup! T2
+ %bw!
+endfunc
+
" Test for jumping to last accessed tabpage
func Test_lastused_tabpage()
tabonly!
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 11e32067b2..1dd656ece5 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -5,12 +5,57 @@ source screendump.vim
" SEGV occurs in older versions. (At least 7.4.1748 or older)
func Test_ptag_with_notagstack()
+ CheckFeature quickfix
+
set notagstack
call assert_fails('ptag does_not_exist_tag_name', 'E426')
set tagstack&vim
endfunc
+func Test_ptjump()
+ CheckFeature quickfix
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ 'Xtags')
+ call writefile(['one', 'two', 'three'], 'Xfile')
+
+ %bw!
+ ptjump two
+ call assert_equal(2, winnr())
+ wincmd p
+ call assert_equal(1, &previewwindow)
+ call assert_equal('Xfile', expand("%:p:t"))
+ call assert_equal(2, line('.'))
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, winnr())
+ close
+ call setline(1, ['one', 'two', 'three'])
+ exe "normal 3G\<C-W>g}"
+ call assert_equal(2, winnr())
+ wincmd p
+ call assert_equal(1, &previewwindow)
+ call assert_equal('Xfile', expand("%:p:t"))
+ call assert_equal(3, line('.'))
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, winnr())
+ close
+ exe "normal 3G5\<C-W>\<C-G>}"
+ wincmd p
+ call assert_equal(5, winheight(0))
+ close
+
+ call delete('Xtags')
+ call delete('Xfile')
+ set tags&
+endfunc
+
func Test_cancel_ptjump()
+ CheckFeature quickfix
+
set tags=Xtags
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
\ "word\tfile1\tcmd1",
@@ -70,6 +115,8 @@ func Test_duplicate_tagjump()
endfunc
func Test_tagjump_switchbuf()
+ CheckFeature quickfix
+
set tags=Xtags
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
\ "second\tXfile1\t2",
@@ -935,6 +982,11 @@ func Test_tag_multimatch()
tag FIRST
tnext
call assert_equal(2, line('.'))
+ tlast
+ tprev
+ call assert_equal(2, line('.'))
+ tNext
+ call assert_equal(1, line('.'))
set ignorecase&
call delete('Xtags')
@@ -1077,6 +1129,217 @@ Type number and <Enter> (q or empty cancels):
%bwipe
endfunc
+" Test for :isearch, :ilist, :ijump and :isplit commands
+" Test for [i, ]i, [I, ]I, [ CTRL-I, ] CTRL-I and CTRL-W i commands
+func Test_inc_search()
+ new
+ call setline(1, ['1:foo', '2:foo', 'foo', '3:foo', '4:foo'])
+ call cursor(3, 1)
+
+ " Test for [i and ]i
+ call assert_equal('1:foo', execute('normal [i'))
+ call assert_equal('2:foo', execute('normal 2[i'))
+ call assert_fails('normal 3[i', 'E387:')
+ call assert_equal('3:foo', execute('normal ]i'))
+ call assert_equal('4:foo', execute('normal 2]i'))
+ call assert_fails('normal 3]i', 'E389:')
+
+ " Test for :isearch
+ call assert_equal('1:foo', execute('isearch foo'))
+ call assert_equal('3:foo', execute('isearch 4 /foo/'))
+ call assert_fails('isearch 3 foo', 'E387:')
+ call assert_equal('3:foo', execute('+1,$isearch foo'))
+ call assert_fails('1,.-1isearch 3 foo', 'E389:')
+ call assert_fails('isearch bar', 'E389:')
+ call assert_fails('isearch /foo/3', 'E488:')
+
+ " Test for [I and ]I
+ call assert_equal([
+ \ ' 1: 1 1:foo',
+ \ ' 2: 2 2:foo',
+ \ ' 3: 3 foo',
+ \ ' 4: 4 3:foo',
+ \ ' 5: 5 4:foo'], split(execute('normal [I'), "\n"))
+ call assert_equal([
+ \ ' 1: 4 3:foo',
+ \ ' 2: 5 4:foo'], split(execute('normal ]I'), "\n"))
+
+ " Test for :ilist
+ call assert_equal([
+ \ ' 1: 1 1:foo',
+ \ ' 2: 2 2:foo',
+ \ ' 3: 3 foo',
+ \ ' 4: 4 3:foo',
+ \ ' 5: 5 4:foo'], split(execute('ilist foo'), "\n"))
+ call assert_equal([
+ \ ' 1: 4 3:foo',
+ \ ' 2: 5 4:foo'], split(execute('+1,$ilist /foo/'), "\n"))
+ call assert_fails('ilist bar', 'E389:')
+
+ " Test for [ CTRL-I and ] CTRL-I
+ exe "normal [\t"
+ call assert_equal([1, 3], [line('.'), col('.')])
+ exe "normal 2j4[\t"
+ call assert_equal([4, 3], [line('.'), col('.')])
+ call assert_fails("normal k3[\t", 'E387:')
+ call assert_fails("normal 6[\t", 'E389:')
+ exe "normal ]\t"
+ call assert_equal([4, 3], [line('.'), col('.')])
+ exe "normal k2]\t"
+ call assert_equal([5, 3], [line('.'), col('.')])
+ call assert_fails("normal 2k3]\t", 'E389:')
+
+ " Test for :ijump
+ call cursor(3, 1)
+ ijump foo
+ call assert_equal([1, 3], [line('.'), col('.')])
+ call cursor(3, 1)
+ ijump 4 /foo/
+ call assert_equal([4, 3], [line('.'), col('.')])
+ call cursor(3, 1)
+ call assert_fails('ijump 3 foo', 'E387:')
+ +,$ijump 2 foo
+ call assert_equal([5, 3], [line('.'), col('.')])
+ call assert_fails('ijump bar', 'E389:')
+
+ " Test for CTRL-W i
+ call cursor(3, 1)
+ wincmd i
+ call assert_equal([1, 3, 3], [line('.'), col('.'), winnr('$')])
+ close
+ 5wincmd i
+ call assert_equal([5, 3, 3], [line('.'), col('.'), winnr('$')])
+ close
+ call assert_fails('3wincmd i', 'E387:')
+ call assert_fails('6wincmd i', 'E389:')
+
+ " Test for :isplit
+ isplit foo
+ call assert_equal([1, 3, 3], [line('.'), col('.'), winnr('$')])
+ close
+ isplit 5 /foo/
+ call assert_equal([5, 3, 3], [line('.'), col('.'), winnr('$')])
+ close
+ call assert_fails('isplit 3 foo', 'E387:')
+ call assert_fails('isplit 6 foo', 'E389:')
+ call assert_fails('isplit bar', 'E389:')
+
+ close!
+endfunc
+
+" this was using a line from ml_get() freed by the regexp
+func Test_isearch_copy_line()
+ new
+ norm o
+ norm 0
+ 0norm o
+ sil! norm bc0
+ sil! isearch \%')
+ bwipe!
+endfunc
+
+" Test for :dsearch, :dlist, :djump and :dsplit commands
+" Test for [d, ]d, [D, ]D, [ CTRL-D, ] CTRL-D and CTRL-W d commands
+func Test_macro_search()
+ new
+ call setline(1, ['#define FOO 1', '#define FOO 2', '#define FOO 3',
+ \ '#define FOO 4', '#define FOO 5'])
+ call cursor(3, 9)
+
+ " Test for [d and ]d
+ call assert_equal('#define FOO 1', execute('normal [d'))
+ call assert_equal('#define FOO 2', execute('normal 2[d'))
+ call assert_fails('normal 3[d', 'E387:')
+ call assert_equal('#define FOO 4', execute('normal ]d'))
+ call assert_equal('#define FOO 5', execute('normal 2]d'))
+ call assert_fails('normal 3]d', 'E388:')
+
+ " Test for :dsearch
+ call assert_equal('#define FOO 1', execute('dsearch FOO'))
+ call assert_equal('#define FOO 5', execute('dsearch 5 /FOO/'))
+ call assert_fails('dsearch 3 FOO', 'E387:')
+ call assert_equal('#define FOO 4', execute('+1,$dsearch FOO'))
+ call assert_fails('1,.-1dsearch 3 FOO', 'E388:')
+ call assert_fails('dsearch BAR', 'E388:')
+
+ " Test for [D and ]D
+ call assert_equal([
+ \ ' 1: 1 #define FOO 1',
+ \ ' 2: 2 #define FOO 2',
+ \ ' 3: 3 #define FOO 3',
+ \ ' 4: 4 #define FOO 4',
+ \ ' 5: 5 #define FOO 5'], split(execute('normal [D'), "\n"))
+ call assert_equal([
+ \ ' 1: 4 #define FOO 4',
+ \ ' 2: 5 #define FOO 5'], split(execute('normal ]D'), "\n"))
+
+ " Test for :dlist
+ call assert_equal([
+ \ ' 1: 1 #define FOO 1',
+ \ ' 2: 2 #define FOO 2',
+ \ ' 3: 3 #define FOO 3',
+ \ ' 4: 4 #define FOO 4',
+ \ ' 5: 5 #define FOO 5'], split(execute('dlist FOO'), "\n"))
+ call assert_equal([
+ \ ' 1: 4 #define FOO 4',
+ \ ' 2: 5 #define FOO 5'], split(execute('+1,$dlist /FOO/'), "\n"))
+ call assert_fails('dlist BAR', 'E388:')
+
+ " Test for [ CTRL-D and ] CTRL-D
+ exe "normal [\<C-D>"
+ call assert_equal([1, 9], [line('.'), col('.')])
+ exe "normal 2j4[\<C-D>"
+ call assert_equal([4, 9], [line('.'), col('.')])
+ call assert_fails("normal k3[\<C-D>", 'E387:')
+ call assert_fails("normal 6[\<C-D>", 'E388:')
+ exe "normal ]\<C-D>"
+ call assert_equal([4, 9], [line('.'), col('.')])
+ exe "normal k2]\<C-D>"
+ call assert_equal([5, 9], [line('.'), col('.')])
+ call assert_fails("normal 2k3]\<C-D>", 'E388:')
+
+ " Test for :djump
+ call cursor(3, 9)
+ djump FOO
+ call assert_equal([1, 9], [line('.'), col('.')])
+ call cursor(3, 9)
+ djump 4 /FOO/
+ call assert_equal([4, 9], [line('.'), col('.')])
+ call cursor(3, 9)
+ call assert_fails('djump 3 FOO', 'E387:')
+ +,$djump 2 FOO
+ call assert_equal([5, 9], [line('.'), col('.')])
+ call assert_fails('djump BAR', 'E388:')
+
+ " Test for CTRL-W d
+ call cursor(3, 9)
+ wincmd d
+ call assert_equal([1, 9, 3], [line('.'), col('.'), winnr('$')])
+ close
+ 5wincmd d
+ call assert_equal([5, 9, 3], [line('.'), col('.'), winnr('$')])
+ close
+ call assert_fails('3wincmd d', 'E387:')
+ call assert_fails('6wincmd d', 'E388:')
+ new
+ call assert_fails("normal \<C-W>d", 'E349:')
+ call assert_fails("normal \<C-W>\<C-D>", 'E349:')
+ close
+
+ " Test for :dsplit
+ dsplit FOO
+ call assert_equal([1, 9, 3], [line('.'), col('.'), winnr('$')])
+ close
+ dsplit 5 /FOO/
+ call assert_equal([5, 9, 3], [line('.'), col('.'), winnr('$')])
+ close
+ call assert_fails('dsplit 3 FOO', 'E387:')
+ call assert_fails('dsplit 6 FOO', 'E388:')
+ call assert_fails('dsplit BAR', 'E388:')
+
+ close!
+endfunc
+
func Test_define_search()
" this was accessing freed memory
new
@@ -1092,6 +1355,37 @@ func Test_define_search()
bwipe!
endfunc
+" Test for [*, [/, ]* and ]/
+func Test_comment_search()
+ new
+ call setline(1, ['', '/*', ' *', ' *', ' */'])
+ normal! 4gg[/
+ call assert_equal([2, 1], [line('.'), col('.')])
+ normal! 3gg[*
+ call assert_equal([2, 1], [line('.'), col('.')])
+ normal! 3gg]/
+ call assert_equal([5, 3], [line('.'), col('.')])
+ normal! 3gg]*
+ call assert_equal([5, 3], [line('.'), col('.')])
+ %d
+ call setline(1, ['', '/*', ' *', ' *'])
+ call assert_beeps('normal! 3gg]/')
+ %d
+ call setline(1, ['', ' *', ' *', ' */'])
+ call assert_beeps('normal! 4gg[/')
+ %d
+ call setline(1, ' /* comment */')
+ normal! 15|[/
+ call assert_equal(9, col('.'))
+ normal! 15|]/
+ call assert_equal(21, col('.'))
+ call setline(1, ' comment */')
+ call assert_beeps('normal! 15|[/')
+ call setline(1, ' /* comment')
+ call assert_beeps('normal! 15|]/')
+ close!
+endfunc
+
" Test for the 'taglength' option
func Test_tag_length()
set tags=Xtags
diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim
index e11815ff33..be46773256 100644
--- a/src/nvim/testdir/test_taglist.vim
+++ b/src/nvim/testdir/test_taglist.vim
@@ -1,5 +1,7 @@
" test taglist(), tagfiles() functions and :tags command
+source view_util.vim
+
func Test_taglist()
call writefile([
\ "FFoo\tXfoo\t1",
@@ -222,3 +224,21 @@ func Test_format_error()
set tags&
call delete('Xtags')
endfunc
+
+" Test for :tag command completion with 'wildoptions' set to 'tagfile'
+func Test_tag_complete_wildoptions()
+ call writefile(["foo\ta.c\t10;\"\tf", "bar\tb.c\t20;\"\td"], 'Xtags')
+ set tags=Xtags
+ set wildoptions=tagfile
+
+ call feedkeys(":tag \<C-D>\<C-R>=Screenline(&lines - 1)\<CR> : "
+ \ .. "\<C-R>=Screenline(&lines - 2)\<CR>\<C-B>\"\<CR>", 'xt')
+
+ call assert_equal('"tag bar d b.c : foo f a.c', @:)
+
+ call delete('Xtags')
+ set wildoptions&
+ set tags&
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_termcodes.vim b/src/nvim/testdir/test_termcodes.vim
index f3b10a922e..c0712aa892 100644
--- a/src/nvim/testdir/test_termcodes.vim
+++ b/src/nvim/testdir/test_termcodes.vim
@@ -1,4 +1,38 @@
+" Test for terminal keycodes that doesn't have termcap entries
+func Test_special_term_keycodes()
+ new
+ " Test for <xHome>, <S-xHome> and <C-xHome>
+ " send <K_SPECIAL> <KS_EXTRA> keycode
+ call feedkeys("i\<C-K>\x80\xfd\x3f\n", 'xt')
+ " send <K_SPECIAL> <KS_MODIFIER> bitmap <K_SPECIAL> <KS_EXTRA> keycode
+ call feedkeys("i\<C-K>\x80\xfc\x2\x80\xfd\x3f\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x4\x80\xfd\x3f\n", 'xt')
+ " Test for <xEnd>, <S-xEnd> and <C-xEnd>
+ call feedkeys("i\<C-K>\x80\xfd\x3d\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x2\x80\xfd\x3d\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x4\x80\xfd\x3d\n", 'xt')
+ " Test for <zHome>, <S-zHome> and <C-zHome>
+ call feedkeys("i\<C-K>\x80\xfd\x40\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x2\x80\xfd\x40\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x4\x80\xfd\x40\n", 'xt')
+ " Test for <zEnd>, <S-zEnd> and <C-zEnd>
+ call feedkeys("i\<C-K>\x80\xfd\x3e\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x2\x80\xfd\x3e\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x4\x80\xfd\x3e\n", 'xt')
+ " Test for <xUp>, <xDown>, <xLeft> and <xRight>
+ call feedkeys("i\<C-K>\x80\xfd\x41\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfd\x42\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfd\x43\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfd\x44\n", 'xt')
+ call assert_equal(['<Home>', '<S-Home>', '<C-Home>',
+ \ '<End>', '<S-End>', '<C-End>',
+ \ '<Home>', '<S-Home>', '<C-Home>',
+ \ '<End>', '<S-End>', '<C-End>',
+ \ '<Up>', '<Down>', '<Left>', '<Right>', ''], getline(1, '$'))
+ bw!
+endfunc
+
func Test_simplify_ctrl_at()
" feeding unsimplified CTRL-@ should still trigger i_CTRL-@
call feedkeys("ifoo\<Esc>A\<*C-@>x", 'xt')
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
index 748af199b2..970f5ae0d0 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -1060,7 +1060,7 @@ func Test_tw_2_fo_tm_replace()
endfunc
" Test for 'matchpairs' with multibyte chars
-func Test_mps()
+func Test_mps_multibyte()
new
let t =<< trim END
{
@@ -1084,6 +1084,30 @@ func Test_mps()
bwipe!
endfunc
+" Test for 'matchpairs' in latin1 encoding
+func Test_mps_latin1()
+ new
+ let save_enc = &encoding
+ " set encoding=latin1
+ call setline(1, 'abc(def)ghi')
+ normal %
+ call assert_equal(8, col('.'))
+ normal %
+ call assert_equal(4, col('.'))
+ call cursor(1, 6)
+ normal [(
+ call assert_equal(4, col('.'))
+ normal %
+ call assert_equal(8, col('.'))
+ call cursor(1, 6)
+ normal ])
+ call assert_equal(8, col('.'))
+ normal %
+ call assert_equal(4, col('.'))
+ let &encoding = save_enc
+ close!
+endfunc
+
func Test_empty_matchpairs()
split
set matchpairs= showmatch
@@ -1137,8 +1161,103 @@ func Test_whichwrap_multi_byte()
bwipe!
endfunc
-func Test_substitute()
- call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
+" Test for automatically adding comment leaders in insert mode
+func Test_threepiece_comment()
+ new
+ setlocal expandtab
+ call setline(1, ["\t/*"])
+ setlocal formatoptions=croql
+ call cursor(1, 3)
+ call feedkeys("A\<cr>\<cr>/", 'tnix')
+ call assert_equal(["\t/*", " *", " */"], getline(1, '$'))
+
+ " If a comment ends in a single line, then don't add it in the next line
+ %d
+ call setline(1, '/* line1 */')
+ call feedkeys("A\<CR>next line", 'xt')
+ call assert_equal(['/* line1 */', 'next line'], getline(1, '$'))
+
+ %d
+ " Copy the trailing indentation from the leader comment to a new line
+ setlocal autoindent noexpandtab
+ call feedkeys("a\t/*\tone\ntwo\n/", 'xt')
+ call assert_equal(["\t/*\tone", "\t *\ttwo", "\t */"], getline(1, '$'))
+ close!
+endfunc
+
+" Test for the 'f' flag in 'comments' (only the first line has the comment
+" string)
+func Test_firstline_comment()
+ new
+ setlocal comments=f:- fo+=ro
+ exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
+ call assert_equal(['A', '- B', ' C', ' D'], getline(1, '$'))
+ %d
+ setlocal comments=:-
+ exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
+ call assert_equal(['- A', '- B', '- C', '- D'], getline(1, '$'))
+ %bw!
+endfunc
+
+" Test for the 'r' flag in 'comments' (right align comment)
+func Test_comment_rightalign()
+ new
+ setlocal comments=sr:/***,m:**,ex-2:******/ fo+=ro
+ exe "normal i=\<C-C>o\t /***\nD\n/"
+ exe "normal 2GOA\<C-C>joB\<C-C>jOC\<C-C>joE\<C-C>GOF\<C-C>joG"
+ let expected =<< trim END
+ =
+ A
+ /***
+ ** B
+ ** C
+ ** D
+ ** E
+ ** F
+ ******/
+ G
+ END
+ call assert_equal(expected, getline(1, '$'))
+ %bw!
+endfunc
+
+" Test for the 'b' flag in 'comments'
+func Test_comment_blank()
+ new
+ setlocal comments=b:* fo+=ro
+ exe "normal i* E\nF\n\<BS>G\nH\<C-C>ggOC\<C-C>O\<BS>B\<C-C>OA\<C-C>2joD"
+ let expected =<< trim END
+ A
+ *B
+ * C
+ * D
+ * E
+ * F
+ *G
+ H
+ END
+ call assert_equal(expected, getline(1, '$'))
+ %bw!
+endfunc
+
+" Test for the 'n' flag in comments
+func Test_comment_nested()
+ new
+ setlocal comments=n:> fo+=ro
+ exe "normal i> B\nD\<C-C>ggOA\<C-C>joC\<C-C>Go\<BS>>>> F\nH"
+ exe "normal 5GOE\<C-C>6GoG"
+ let expected =<< trim END
+ > A
+ > B
+ > C
+ > D
+ >>>> E
+ >>>> F
+ >>>> G
+ >>>> H
+ END
+ call assert_equal(expected, getline(1, '$'))
+ %bw!
endfunc
" Test for 'a' and 'w' flags in 'formatoptions'
diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim
index 81f2fea026..eeb2946a8b 100644
--- a/src/nvim/testdir/test_textobjects.vim
+++ b/src/nvim/testdir/test_textobjects.vim
@@ -1,8 +1,7 @@
" Test for textobjects
-if !has('textobjects')
- finish
-endif
+source check.vim
+CheckFeature textobjects
func CpoM(line, useM, expected)
new
@@ -234,6 +233,10 @@ func Test_empty_html_tag()
normal 0f<vitsaaa
call assert_equal('aaa', getline(1))
+ " selecting a tag block in an non-empty blank line should fail
+ call setline(1, ' ')
+ call assert_beeps('normal $vaty')
+
bwipe!
endfunc
@@ -368,6 +371,168 @@ func Test_sentence_with_cursor_on_delimiter()
%delete _
endfunc
+" Test for the paragraph (ap) text object
+func Test_paragraph()
+ new
+ call setline(1, ['First line.', 'Second line.', 'Third line.'])
+ call cursor(2, 1)
+ normal vapy
+ call assert_equal("First line.\nSecond line.\nThird line.\n", @")
+
+ call cursor(2, 1)
+ call assert_beeps('normal vapapy')
+
+ call setline(1, ['First line.', 'Second line.', ' ', ''])
+ call cursor(1, 1)
+ normal vapy
+ call assert_equal("First line.\nSecond line.\n \n\n", @")
+
+ call setline(1, ['', '', '', 'First line.', 'Second line.'])
+ call cursor(2, 1)
+ normal yap
+ call assert_equal("\n\n\nFirst line.\nSecond line.\n", @")
+ call assert_beeps('normal 3yap')
+ exe "normal \<C-C>"
+
+ %d
+ call setline(1, [' ', ' ', ' '])
+ call cursor(2, 1)
+ normal Vipy
+ call assert_equal(" \n \n \n", @")
+ call cursor(2, 1)
+ call assert_beeps("normal Vipip")
+ exe "normal \<C-C>"
+
+ close!
+endfunc
+
+" Tests for text object aw
+func Test_textobj_a_word()
+ new
+ call append(0, ['foobar,eins,foobar', 'foo,zwei,foo '])
+ " diw
+ norm! 1gg0diw
+ call assert_equal([',eins,foobar', 'foo,zwei,foo ', ''], getline(1,'$'))
+ " daw
+ norm! 2ggEdaw
+ call assert_equal([',eins,foobar', 'foo,zwei,', ''], getline(1, '$'))
+ " daw the last word in a line
+ call setline(1, ['foo bar', 'foo bar', ''])
+ call cursor(1, 5)
+ normal daw
+ call assert_equal('foo', getline(1))
+ " aw in visual mode
+ call cursor(2, 5)
+ normal! vawx
+ call assert_equal('foo', getline(2))
+ %d
+ call append(0, ["foo\teins\tfoobar", "foo\tzwei\tfoo "])
+ " diW
+ norm! 2ggwd2iW
+ call assert_equal(['foo eins foobar', 'foo foo ', ''], getline(1,'$'))
+ " daW
+ norm! 1ggd2aW
+ call assert_equal(['foobar', 'foo foo ', ''], getline(1,'$'))
+
+ %d
+ call append(0, ["foo\teins\tfoobar", "foo\tzwei\tfoo "])
+ " aw in visual line mode switches to characterwise mode
+ norm! 2gg$Vawd
+ call assert_equal(['foo eins foobar', 'foo zwei foo'], getline(1,'$'))
+ norm! 1gg$Viwd
+ call assert_equal(['foo eins ', 'foo zwei foo'], getline(1,'$'))
+
+ " visually selecting a tab before a word with 'selection' set to 'exclusive'
+ set selection=exclusive
+ normal gg3lvlawy
+ call assert_equal("\teins", @")
+ " visually selecting a tab before a word with 'selection' set to 'inclusive'
+ set selection=inclusive
+ normal gg3lvlawy
+ call assert_equal("\teins\t", @")
+ set selection&
+
+ " selecting a word with no non-space characters in a buffer fails
+ %d
+ call setline(1, ' ')
+ call assert_beeps('normal 3lyaw')
+
+ " visually selecting words backwards with no more words to select
+ call setline(1, 'one two')
+ call assert_beeps('normal 2lvh2aw')
+ exe "normal \<C-C>"
+ call assert_beeps('normal $vh3aw')
+ exe "normal \<C-C>"
+ call setline(1, ['', 'one two'])
+ call assert_beeps('normal 2G2lvh3aw')
+ exe "normal \<C-C>"
+
+ " selecting words forward with no more words to select
+ %d
+ call setline(1, 'one a')
+ call assert_beeps('normal 0y3aw')
+ call setline(1, 'one two ')
+ call assert_beeps('normal 0y3aw')
+ call assert_beeps('normal 03ly2aw')
+
+ " clean up
+ bw!
+endfunc
+
+" Test for is and as text objects
+func Test_textobj_sentence()
+ new
+ call append(0, ['This is a test. With some sentences!', '',
+ \ 'Even with a question? And one more. And no sentence here'])
+ " Test for dis - does not remove trailing whitespace
+ norm! 1gg0dis
+ call assert_equal([' With some sentences!', '',
+ \ 'Even with a question? And one more. And no sentence here', ''],
+ \ getline(1,'$'))
+ " Test for das - removes leading whitespace
+ norm! 3ggf?ldas
+ call assert_equal([' With some sentences!', '',
+ \ 'Even with a question? And no sentence here', ''], getline(1,'$'))
+ " when used in visual mode, is made characterwise
+ norm! 3gg$Visy
+ call assert_equal('v', visualmode())
+ " reset visualmode()
+ norm! 3ggVy
+ norm! 3gg$Vasy
+ call assert_equal('v', visualmode())
+ " basic testing for textobjects a< and at
+ %d
+ call setline(1, ['<div> ','<a href="foobar" class="foo">xyz</a>',' </div>', ' '])
+ " a<
+ norm! 1gg0da<
+ call assert_equal([' ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
+ norm! 1pj
+ call assert_equal([' <div>', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
+ " at
+ norm! d2at
+ call assert_equal([' '], getline(1,'$'))
+ %d
+ call setline(1, ['<div> ','<a href="foobar" class="foo">xyz</a>',' </div>', ' '])
+ " i<
+ norm! 1gg0di<
+ call assert_equal(['<> ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
+ norm! 1Pj
+ call assert_equal(['<div> ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
+ norm! d2it
+ call assert_equal(['<div></div>',' '], getline(1,'$'))
+ " basic testing for a[ and i[ text object
+ %d
+ call setline(1, [' ', '[', 'one [two]', 'thre', ']'])
+ norm! 3gg0di[
+ call assert_equal([' ', '[', ']'], getline(1,'$'))
+ call setline(1, [' ', '[', 'one [two]', 'thre', ']'])
+ norm! 3gg0ftd2a[
+ call assert_equal([' '], getline(1,'$'))
+
+ " clean up
+ bw!
+endfunc
+
" Test for quote (', " and `) textobjects
func Test_textobj_quote()
new
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index 09eed4e10d..56a5ec96af 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -1,13 +1,16 @@
" Test for timers
-if !has('timers')
- finish
-endif
+source check.vim
+CheckFeature timers
source shared.vim
source term_util.vim
source load.vim
+func SetUp()
+ call timer_stopall()
+endfunc
+
func MyHandler(timer)
let g:val += 1
endfunc
@@ -16,7 +19,7 @@ func MyHandlerWithLists(lists, timer)
let x = string(a:lists)
endfunc
-func Test_oneshot()
+func Test_timer_oneshot()
let g:val = 0
let timer = timer_start(50, 'MyHandler')
let slept = WaitFor('g:val == 1')
@@ -28,7 +31,7 @@ func Test_oneshot()
endif
endfunc
-func Test_repeat_three()
+func Test_timer_repeat_three()
let g:val = 0
let timer = timer_start(50, 'MyHandler', {'repeat': 3})
let slept = WaitFor('g:val == 3')
@@ -40,8 +43,7 @@ func Test_repeat_three()
endif
endfunc
-func Test_repeat_many()
- call timer_stopall()
+func Test_timer_repeat_many()
let g:val = 0
let timer = timer_start(50, 'MyHandler', {'repeat': -1})
if has('mac')
@@ -52,7 +54,7 @@ func Test_repeat_many()
call assert_inrange((has('mac') ? 1 : 2), LoadAdjust(5), g:val)
endfunc
-func Test_with_partial_callback()
+func Test_timer_with_partial_callback()
let g:val = 0
let meow = {'one': 1}
function meow.bite(...)
@@ -69,13 +71,13 @@ func Test_with_partial_callback()
endif
endfunc
-func Test_retain_partial()
+func Test_timer_retain_partial()
call timer_start(50, function('MyHandlerWithLists', [['a']]))
- call garbagecollect()
+ call test_garbagecollect_now()
sleep 100m
endfunc
-func Test_info()
+func Test_timer_info()
let id = timer_start(1000, 'MyHandler')
let info = id->timer_info()
call assert_equal(id, info[0]['id'])
@@ -92,10 +94,11 @@ func Test_info()
call timer_stop(id)
call assert_equal([], timer_info(id))
+
+ call assert_fails('call timer_info("abc")', 'E39:')
endfunc
-func Test_stopall()
- call timer_stopall()
+func Test_timer_stopall()
let id1 = timer_start(1000, 'MyHandler')
let id2 = timer_start(2000, 'MyHandler')
let info = timer_info()
@@ -106,7 +109,7 @@ func Test_stopall()
call assert_equal(0, len(info))
endfunc
-func Test_paused()
+func Test_timer_paused()
let g:val = 0
let id = timer_start(50, 'MyHandler')
@@ -130,6 +133,8 @@ func Test_paused()
else
call assert_inrange(0, 10, slept)
endif
+
+ call assert_fails('call timer_pause("abc", 1)', 'E39:')
endfunc
func StopMyself(timer)
@@ -139,7 +144,7 @@ func StopMyself(timer)
endif
endfunc
-func Test_delete_myself()
+func Test_timer_delete_myself()
let g:called = 0
let t = timer_start(10, 'StopMyself', {'repeat': -1})
call WaitForAssert({-> assert_equal(2, g:called)})
@@ -151,33 +156,45 @@ func StopTimer1(timer)
let g:timer2 = 10->timer_start('StopTimer2')
" avoid maxfuncdepth error
call timer_pause(g:timer1, 1)
- sleep 40m
+ sleep 20m
endfunc
func StopTimer2(timer)
call timer_stop(g:timer1)
endfunc
-func Test_stop_in_callback()
+func Test_timer_stop_in_callback()
+ call assert_equal(0, len(timer_info()))
let g:timer1 = timer_start(10, 'StopTimer1')
- sleep 40m
+ let slept = 0
+ for i in range(10)
+ if len(timer_info()) == 0
+ break
+ endif
+ sleep 10m
+ let slept += 10
+ endfor
+ " This should take only 30 msec, but on Mac it's often longer
+ call assert_inrange(0, 50, slept)
endfunc
func StopTimerAll(timer)
call timer_stopall()
endfunc
-func Test_stop_all_in_callback()
- call timer_stopall()
- let g:timer1 = timer_start(10, 'StopTimerAll')
- let info = timer_info()
- call assert_equal(1, len(info))
- if has('mac')
- sleep 100m
- endif
- sleep 40m
- let info = timer_info()
- call assert_equal(0, len(info))
+func Test_timer_stop_all_in_callback()
+ call assert_equal(0, len(timer_info()))
+ call timer_start(10, 'StopTimerAll')
+ call assert_equal(1, len(timer_info()))
+ let slept = 0
+ for i in range(10)
+ if len(timer_info()) == 0
+ break
+ endif
+ sleep 10m
+ let slept += 10
+ endfor
+ call assert_inrange(0, 30, slept)
endfunc
func FeedkeysCb(timer)
@@ -190,7 +207,7 @@ func InputCb(timer)
call Resume()
endfunc
-func Test_input_in_timer()
+func Test_timer_input_in_timer()
let g:val = ''
call timer_start(10, 'InputCb')
call Standby(1000)
@@ -212,6 +229,10 @@ func Test_timer_errors()
call WaitForAssert({-> assert_equal(3, g:call_count)})
sleep 50m
call assert_equal(3, g:call_count)
+
+ call assert_fails('call timer_start(100, "MyHandler", "abc")', 'E475:')
+ call assert_fails('call timer_start(100, [])', 'E921:')
+ call assert_fails('call timer_stop("abc")', 'E39:')
endfunc
func FuncWithCaughtError(timer)
@@ -243,7 +264,7 @@ func Interrupt(timer)
call nvim_input("\<C-C>")
endfunc
-func Test_peek_and_get_char()
+func Test_timer_peek_and_get_char()
if !has('unix') && !has('gui_running')
return
endif
@@ -254,7 +275,7 @@ func Test_peek_and_get_char()
eval intr->timer_stop()
endfunc
-func Test_getchar_zero()
+func Test_timer_getchar_zero()
if has('win32') && !has('gui_running')
" Console: no low-level input
return
@@ -272,20 +293,20 @@ func Test_getchar_zero()
call timer_stop(id)
endfunc
-func Test_ex_mode()
+func Test_timer_ex_mode()
" Function with an empty line.
func Foo(...)
endfunc
let timer = timer_start(40, function('g:Foo'), {'repeat':-1})
" This used to throw error E749.
- exe "normal gQsleep 100m\rvi\r"
+ exe "normal Qsleep 100m\rvi\r"
call timer_stop(timer)
endfunc
-func Test_restore_count()
+func Test_timer_restore_count()
if !CanRunVimInTerminal()
- return
+ throw 'Skipped: cannot run Vim in a terminal window'
endif
" Check that v:count is saved and restored, not changed by a timer.
call writefile([
@@ -316,9 +337,9 @@ endfunc
" Test that the garbage collector isn't triggered if a timer callback invokes
" vgetc().
-func Test_nocatch_garbage_collect()
- CheckFunction test_garbagecollect_soon
- CheckFunction test_override
+func Test_timer_nocatch_garbage_collect()
+ " skipped: Nvim does not support test_garbagecollect_soon(), test_override()
+ return
" 'uptimetime. must be bigger than the timer timeout
set ut=200
call test_garbagecollect_soon()
@@ -340,7 +361,7 @@ func Test_nocatch_garbage_collect()
delfunc FeedChar
endfunc
-func Test_error_in_timer_callback()
+func Test_timer_error_in_timer_callback()
if !has('terminal') || (has('win32') && has('gui_running'))
throw 'Skipped: cannot run Vim in a terminal window'
endif
@@ -375,6 +396,15 @@ func Test_error_in_timer_callback()
exe buf .. 'bwipe!'
endfunc
+" Test for garbage collection when a timer is still running
+func Test_timer_garbage_collect()
+ let timer = timer_start(1000, function('MyHandler'), {'repeat' : 10})
+ call test_garbagecollect_now()
+ let l = timer_info(timer)
+ call assert_equal(function('MyHandler'), l[0].callback)
+ call timer_stop(timer)
+endfunc
+
func Test_timer_invalid_callback()
call assert_fails('call timer_start(0, "0")', 'E921')
endfunc
diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim
index 205ed095ea..598402fafe 100644
--- a/src/nvim/testdir/test_trycatch.vim
+++ b/src/nvim/testdir/test_trycatch.vim
@@ -1996,6 +1996,47 @@ func Test_reload_in_try_catch()
call delete('Xreload')
endfunc
+" Test for errors with :catch, :throw, :finally {{{1
+func Test_try_catch_errors()
+ call assert_fails('throw |', 'E471:')
+ call assert_fails("throw \n ", 'E471:')
+ call assert_fails('catch abc', 'E603:')
+ call assert_fails('try | let i = 1| finally | catch | endtry', 'E604:')
+ call assert_fails('finally', 'E606:')
+ call assert_fails('try | finally | finally | endtry', 'E607:')
+ " v8.2.3486 has been ported, but v8.2.1183 hasn't, so E170 appears here.
+ " call assert_fails('try | for i in range(5) | endif | endtry', 'E580:')
+ call assert_fails('try | for i in range(5) | endif | endtry', 'E170:')
+ call assert_fails('try | while v:true | endtry', 'E170:')
+ call assert_fails('try | if v:true | endtry', 'E171:')
+endfunc
+
+" Test for verbose messages with :try :catch, and :finally {{{1
+func Test_try_catch_verbose()
+ " This test works only when the language is English
+ if v:lang != "C" && v:lang !~ '^[Ee]n'
+ return
+ endif
+
+ set verbose=14
+ redir => msg
+ try
+ echo i
+ catch /E121:/
+ finally
+ endtry
+ redir END
+ let expected = [
+ \ 'Exception thrown: Vim(echo):E121: Undefined variable: i',
+ \ '',
+ \ 'Exception caught: Vim(echo):E121: Undefined variable: i',
+ \ '',
+ \ 'Exception finished: Vim(echo):E121: Undefined variable: i'
+ \ ]
+ call assert_equal(expected, split(msg, "\n"))
+ set verbose&
+endfunc
+
" Test for using throw in a called function with following error {{{1
func Test_user_command_throw_in_function_call()
let lines =<< trim END
diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim
index 848860649e..a9ec405aa4 100644
--- a/src/nvim/testdir/test_undo.vim
+++ b/src/nvim/testdir/test_undo.vim
@@ -3,8 +3,6 @@
" undo-able pieces. Do that by setting 'undolevels'.
" Also tests :earlier and :later.
-source check.vim
-
func Test_undotree()
new
@@ -137,8 +135,7 @@ func BackOne(expected)
endfunc
func Test_undo_del_chars()
- CheckFunction test_settime
-
+ throw 'Skipped: Nvim does not support test_settime()'
" Setup a buffer without creating undo entries
new
set ul=-1
@@ -299,6 +296,8 @@ func Test_undo_write()
close!
call delete('Xtest')
bwipe! Xtest
+
+ call assert_fails('earlier xyz', 'E475:')
endfunc
func Test_insert_expr()
@@ -332,8 +331,9 @@ func Test_insert_expr()
endfunc
func Test_undofile_earlier()
- CheckFunction test_settime
-
+ throw 'Skipped: Nvim does not support test_settime()'
+ " Issue #1254
+ " create undofile with timestamps older than Vim startup time.
let t0 = localtime() - 43200
call test_settime(t0)
new Xfile
@@ -366,7 +366,7 @@ func Test_wundo_errors()
bwipe!
endfunc
-" Check that reading a truncted undo file doesn't hang.
+" Check that reading a truncated undo file doesn't hang.
func Test_undofile_truncated()
new
call setline(1, 'hello')
@@ -429,6 +429,59 @@ func Test_cmd_in_reg_undo()
let @a = ''
endfunc
+" This used to cause an illegal memory access
+func Test_undo_append()
+ new
+ call feedkeys("axx\<Esc>v", 'xt')
+ undo
+ norm o
+ quit
+endfunc
+
+func Test_undo_0()
+ new
+ set ul=100
+ normal i1
+ undo
+ normal i2
+ undo
+ normal i3
+
+ undo 0
+ let d = undotree()
+ call assert_equal('', getline(1))
+ call assert_equal(0, d.seq_cur)
+
+ redo
+ let d = undotree()
+ call assert_equal('3', getline(1))
+ call assert_equal(3, d.seq_cur)
+
+ undo 2
+ undo 0
+ let d = undotree()
+ call assert_equal('', getline(1))
+ call assert_equal(0, d.seq_cur)
+
+ redo
+ let d = undotree()
+ call assert_equal('2', getline(1))
+ call assert_equal(2, d.seq_cur)
+
+ undo 1
+ undo 0
+ let d = undotree()
+ call assert_equal('', getline(1))
+ call assert_equal(0, d.seq_cur)
+
+ redo
+ let d = undotree()
+ call assert_equal('1', getline(1))
+ call assert_equal(1, d.seq_cur)
+
+ bwipe!
+endfunc
+
" undo or redo are noop if there is nothing to undo or redo
func Test_undo_redo_noop()
new
@@ -454,15 +507,6 @@ func Test_redo_empty_line()
bwipe!
endfunc
-" This used to cause an illegal memory access
-func Test_undo_append()
- new
- call feedkeys("axx\<Esc>v", 'xt')
- undo
- norm o
- quit
-endfunc
-
funct Test_undofile()
" Test undofile() without setting 'undodir'.
if has('persistent_undo')
@@ -504,50 +548,6 @@ funct Test_undofile()
set undodir&
endfunc
-func Test_undo_0()
- new
- set ul=100
- normal i1
- undo
- normal i2
- undo
- normal i3
-
- undo 0
- let d = undotree()
- call assert_equal('', getline(1))
- call assert_equal(0, d.seq_cur)
-
- redo
- let d = undotree()
- call assert_equal('3', getline(1))
- call assert_equal(3, d.seq_cur)
-
- undo 2
- undo 0
- let d = undotree()
- call assert_equal('', getline(1))
- call assert_equal(0, d.seq_cur)
-
- redo
- let d = undotree()
- call assert_equal('2', getline(1))
- call assert_equal(2, d.seq_cur)
-
- undo 1
- undo 0
- let d = undotree()
- call assert_equal('', getline(1))
- call assert_equal(0, d.seq_cur)
-
- redo
- let d = undotree()
- call assert_equal('1', getline(1))
- call assert_equal(1, d.seq_cur)
-
- bwipe!
-endfunc
-
" Tests for the undo file
" Explicitly break changes up in undo-able pieces by setting 'undolevels'.
func Test_undofile_2()
@@ -579,7 +579,7 @@ func Test_undofile_2()
" add 10 lines, delete 6 lines, undo 3
set undofile
- call setbufline(0, 1, ['one', 'two', 'three', 'four', 'five', 'six',
+ call setbufline('%', 1, ['one', 'two', 'three', 'four', 'five', 'six',
\ 'seven', 'eight', 'nine', 'ten'])
set undolevels=100
normal 3Gdd
@@ -733,6 +733,29 @@ func Test_undofile_cryptmethod_blowfish2()
set undofile& undolevels& cryptmethod&
endfunc
+" Test for redoing with incrementing numbered registers
+func Test_redo_repeat_numbered_register()
+ new
+ for [i, v] in [[1, 'one'], [2, 'two'], [3, 'three'],
+ \ [4, 'four'], [5, 'five'], [6, 'six'],
+ \ [7, 'seven'], [8, 'eight'], [9, 'nine']]
+ exe 'let @' .. i .. '="' .. v .. '\n"'
+ endfor
+ call feedkeys('"1p.........', 'xt')
+ call assert_equal(['', 'one', 'two', 'three', 'four', 'five', 'six',
+ \ 'seven', 'eight', 'nine', 'nine'], getline(1, '$'))
+ bwipe!
+endfunc
+
+" Test for redo in insert mode using CTRL-O with multibyte characters
+func Test_redo_multibyte_in_insert_mode()
+ new
+ call feedkeys("a\<C-K>ft", 'xt')
+ call feedkeys("uiHe\<C-O>.llo", 'xt')
+ call assert_equal("He\ufb05llo", getline(1))
+ bwipe!
+endfunc
+
func Test_undo_mark()
new
" The undo is applied to the only line.
diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim
index 596b8b53aa..e37fe43b22 100644
--- a/src/nvim/testdir/test_usercommands.vim
+++ b/src/nvim/testdir/test_usercommands.vim
@@ -56,7 +56,10 @@ function Test_cmdmods()
call assert_equal('lockmarks', g:mods)
loc MyCmd
call assert_equal('lockmarks', g:mods)
- " noautocmd MyCmd
+ noautocmd MyCmd
+ call assert_equal('noautocmd', g:mods)
+ noa MyCmd
+ call assert_equal('noautocmd', g:mods)
noswapfile MyCmd
call assert_equal('noswapfile', g:mods)
nos MyCmd
@@ -70,29 +73,43 @@ function Test_cmdmods()
call assert_equal('silent', g:mods)
sil MyCmd
call assert_equal('silent', g:mods)
+ silent! MyCmd
+ call assert_equal('silent!', g:mods)
+ sil! MyCmd
+ call assert_equal('silent!', g:mods)
tab MyCmd
call assert_equal('tab', g:mods)
topleft MyCmd
call assert_equal('topleft', g:mods)
to MyCmd
call assert_equal('topleft', g:mods)
- " unsilent MyCmd
+ unsilent MyCmd
+ call assert_equal('unsilent', g:mods)
+ uns MyCmd
+ call assert_equal('unsilent', g:mods)
verbose MyCmd
call assert_equal('verbose', g:mods)
verb MyCmd
call assert_equal('verbose', g:mods)
+ 0verbose MyCmd
+ call assert_equal('0verbose', g:mods)
+ 3verbose MyCmd
+ call assert_equal('3verbose', g:mods)
+ 999verbose MyCmd
+ call assert_equal('999verbose', g:mods)
vertical MyCmd
call assert_equal('vertical', g:mods)
vert MyCmd
call assert_equal('vertical', g:mods)
aboveleft belowright botright browse confirm hide keepalt keepjumps
- \ keepmarks keeppatterns lockmarks noswapfile silent tab
- \ topleft verbose vertical MyCmd
+ \ keepmarks keeppatterns lockmarks noautocmd noswapfile silent
+ \ tab topleft unsilent verbose vertical MyCmd
call assert_equal('browse confirm hide keepalt keepjumps ' .
- \ 'keepmarks keeppatterns lockmarks noswapfile silent ' .
- \ 'verbose aboveleft belowright botright tab topleft vertical', g:mods)
+ \ 'keepmarks keeppatterns lockmarks noswapfile unsilent noautocmd ' .
+ \ 'silent verbose aboveleft belowright botright tab topleft vertical',
+ \ g:mods)
let g:mods = ''
command! -nargs=* MyQCmd let g:mods .= '<q-mods> '
@@ -296,7 +313,7 @@ func CustomComplete(A, L, P)
endfunc
func CustomCompleteList(A, L, P)
- return [ "Monday", "Tuesday", "Wednesday" ]
+ return [ "Monday", "Tuesday", "Wednesday", {}]
endfunc
func Test_CmdCompletion()
diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim
index 6af199a512..68fe15ff93 100644
--- a/src/nvim/testdir/test_vartabs.vim
+++ b/src/nvim/testdir/test_vartabs.vim
@@ -1,8 +1,7 @@
" Test for variable tabstops
-if !has("vartabs")
- finish
-endif
+source check.vim
+CheckFeature vartabs
source view_util.vim
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index f93eb6e274..1323288676 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -1771,6 +1771,125 @@ func Test_function_defined_line()
call delete('Xtest.vim')
endfunc
+" Test for missing :endif, :endfor, :endwhile and :endtry {{{1
+func Test_missing_end()
+ call writefile(['if 2 > 1', 'echo ">"'], 'Xscript')
+ call assert_fails('source Xscript', 'E171:')
+ call writefile(['for i in range(5)', 'echo i'], 'Xscript')
+ call assert_fails('source Xscript', 'E170:')
+ call writefile(['while v:true', 'echo "."'], 'Xscript')
+ call assert_fails('source Xscript', 'E170:')
+ call writefile(['try', 'echo "."'], 'Xscript')
+ call assert_fails('source Xscript', 'E600:')
+ call delete('Xscript')
+
+ " Using endfor with :while
+ let caught_e732 = 0
+ try
+ while v:true
+ endfor
+ catch /E732:/
+ let caught_e732 = 1
+ endtry
+ call assert_equal(1, caught_e732)
+
+ " Using endwhile with :for
+ let caught_e733 = 0
+ try
+ for i in range(1)
+ endwhile
+ catch /E733:/
+ let caught_e733 = 1
+ endtry
+ call assert_equal(1, caught_e733)
+
+ " Missing 'in' in a :for statement
+ call assert_fails('for i range(1) | endfor', 'E690:')
+endfunc
+
+" Test for deep nesting of if/for/while/try statements {{{1
+func Test_deep_nest()
+ if !CanRunVimInTerminal()
+ throw 'Skipped: cannot run vim in terminal'
+ endif
+
+ let lines =<< trim [SCRIPT]
+ " Deep nesting of if ... endif
+ func Test1()
+ let @a = join(repeat(['if v:true'], 51), "\n")
+ let @a ..= "\n"
+ let @a ..= join(repeat(['endif'], 51), "\n")
+ @a
+ let @a = ''
+ endfunc
+
+ " Deep nesting of for ... endfor
+ func Test2()
+ let @a = join(repeat(['for i in [1]'], 51), "\n")
+ let @a ..= "\n"
+ let @a ..= join(repeat(['endfor'], 51), "\n")
+ @a
+ let @a = ''
+ endfunc
+
+ " Deep nesting of while ... endwhile
+ func Test3()
+ let @a = join(repeat(['while v:true'], 51), "\n")
+ let @a ..= "\n"
+ let @a ..= join(repeat(['endwhile'], 51), "\n")
+ @a
+ let @a = ''
+ endfunc
+
+ " Deep nesting of try ... endtry
+ func Test4()
+ let @a = join(repeat(['try'], 51), "\n")
+ let @a ..= "\necho v:true\n"
+ let @a ..= join(repeat(['endtry'], 51), "\n")
+ @a
+ let @a = ''
+ endfunc
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+
+ let buf = RunVimInTerminal('-S Xscript', {'rows': 6})
+
+ " Deep nesting of if ... endif
+ call term_sendkeys(buf, ":call Test1()\n")
+ call WaitForAssert({-> assert_match('^E579:', term_getline(buf, 5))})
+
+ " Deep nesting of for ... endfor
+ call term_sendkeys(buf, ":call Test2()\n")
+ call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})
+
+ " Deep nesting of while ... endwhile
+ call term_sendkeys(buf, ":call Test3()\n")
+ call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})
+
+ " Deep nesting of try ... endtry
+ call term_sendkeys(buf, ":call Test4()\n")
+ call WaitForAssert({-> assert_match('^E601:', term_getline(buf, 5))})
+
+ "let l = ''
+ "for i in range(1, 6)
+ " let l ..= term_getline(buf, i) . "\n"
+ "endfor
+ "call assert_report(l)
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+endfunc
+
+" Test for <sfile>, <slnum> in a function {{{1
+func Test_sfile_in_function()
+ func Xfunc()
+ call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('<sfile>'))
+ call assert_equal('2', expand('<slnum>'))
+ endfunc
+ call Xfunc()
+ delfunc Xfunc
+endfunc
+
func Test_for_over_string()
let res = ''
for c in 'aéc̀d'
diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim
index 250b896532..522ca17675 100644
--- a/src/nvim/testdir/test_virtualedit.vim
+++ b/src/nvim/testdir/test_virtualedit.vim
@@ -301,6 +301,9 @@ func Test_replace_on_tab()
call append(0, "'r'\t")
normal gg^5lrxAy
call assert_equal("'r' x y", getline(1))
+ call setline(1, 'aaaaaaaaaaaa')
+ exe "normal! gg2lgR\<Tab>"
+ call assert_equal("aa\taaaa", getline(1))
bwipe!
set virtualedit=
endfunc
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index 41c29c5bb0..f9ac0e0884 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -284,6 +284,15 @@ func Test_virtual_replace2()
call assert_equal(['abcd',
\ 'efgh',
\ 'ijkl'], getline(1, '$'))
+
+ " Test for truncating spaces in a newly added line using 'autoindent' if
+ " characters are not added to that line.
+ %d_
+ call setline(1, [' app', ' bee', ' cat'])
+ setlocal autoindent
+ exe "normal gg$gRt\n\nr"
+ call assert_equal([' apt', '', ' rat'], getline(1, '$'))
+
" clean up
%d_
set bs&vim
@@ -700,7 +709,6 @@ func Test_linewise_select_mode()
exe "normal GkkgH\<Del>"
call assert_equal(['', 'b', 'c'], getline(1, '$'))
-
" linewise select mode: delete middle two lines
call deletebufline('', 1, '$')
call append('$', ['a', 'b', 'c'])
@@ -722,6 +730,15 @@ func Test_linewise_select_mode()
bwipe!
endfunc
+" Test for blockwise select mode (g CTRL-H)
+func Test_blockwise_select_mode()
+ new
+ call setline(1, ['foo', 'bar'])
+ call feedkeys("g\<BS>\<Right>\<Down>mm", 'xt')
+ call assert_equal(['mmo', 'mmr'], getline(1, '$'))
+ close!
+endfunc
+
func Test_visual_mode_put()
new
@@ -1105,6 +1122,96 @@ func Test_block_insert_replace_tabs()
bwipe!
endfunc
+" Test for * register in :
+func Test_star_register()
+ call assert_fails('*bfirst', 'E16:')
+ new
+ call setline(1, ['foo', 'bar', 'baz', 'qux'])
+ exe "normal jVj\<ESC>"
+ *yank r
+ call assert_equal("bar\nbaz\n", @r)
+
+ delmarks < >
+ call assert_fails('*yank', 'E20:')
+ close!
+endfunc
+
+" Test for using visual mode maps in select mode
+func Test_select_mode_map()
+ new
+ vmap <buffer> <F2> 3l
+ call setline(1, 'Test line')
+ call feedkeys("gh\<F2>map", 'xt')
+ call assert_equal('map line', getline(1))
+
+ vmap <buffer> <F2> ygV
+ call feedkeys("0gh\<Right>\<Right>\<F2>cwabc", 'xt')
+ call assert_equal('abc line', getline(1))
+
+ vmap <buffer> <F2> :<C-U>let v=100<CR>
+ call feedkeys("gggh\<Right>\<Right>\<F2>foo", 'xt')
+ call assert_equal('foo line', getline(1))
+
+ " reselect the select mode using gv from a visual mode map
+ vmap <buffer> <F2> gv
+ set selectmode=cmd
+ call feedkeys("0gh\<F2>map", 'xt')
+ call assert_equal('map line', getline(1))
+ set selectmode&
+
+ close!
+endfunc
+
+" Test for changing text in visual mode with 'exclusive' selection
+func Test_exclusive_selection()
+ new
+ call setline(1, ['one', 'two'])
+ set selection=exclusive
+ call feedkeys("vwcabc", 'xt')
+ call assert_equal('abctwo', getline(1))
+ call setline(1, ["\tone"])
+ set virtualedit=all
+ call feedkeys('0v2lcl', 'xt')
+ call assert_equal('l one', getline(1))
+ set virtualedit&
+ set selection&
+ close!
+endfunc
+
+" Test for starting visual mode with a count.
+" This test should be run without any previous visual modes. So this should be
+" run as a first test.
+func Test_AAA_start_visual_mode_with_count()
+ new
+ call setline(1, ['aaaaaaa', 'aaaaaaa', 'aaaaaaa', 'aaaaaaa'])
+ normal! gg2Vy
+ call assert_equal("aaaaaaa\naaaaaaa\n", @")
+ close!
+endfunc
+
+" Test for visually selecting an inner block (iB)
+func Test_visual_inner_block()
+ new
+ call setline(1, ['one', '{', 'two', '{', 'three', '}', 'four', '}', 'five'])
+ call cursor(5, 1)
+ " visually select all the lines in the block and then execute iB
+ call feedkeys("ViB\<C-C>", 'xt')
+ call assert_equal([0, 5, 1, 0], getpos("'<"))
+ call assert_equal([0, 5, 6, 0], getpos("'>"))
+ " visually select two inner blocks
+ call feedkeys("ViBiB\<C-C>", 'xt')
+ call assert_equal([0, 3, 1, 0], getpos("'<"))
+ call assert_equal([0, 7, 5, 0], getpos("'>"))
+ " try to select non-existing inner block
+ call cursor(5, 1)
+ call assert_beeps('normal ViBiBiB')
+ " try to select a unclosed inner block
+ 8,9d
+ call cursor(5, 1)
+ call assert_beeps('normal ViBiB')
+ close!
+endfunc
+
func Test_visual_put_in_block()
new
call setline(1, ['xxxx', 'y∞yy', 'zzzz'])
@@ -1431,5 +1538,17 @@ func Test_visual_paste_clipboard()
bwipe!
endfunc
+func Test_visual_area_adjusted_when_hiding()
+ " The Visual area ended after the end of the line after :hide
+ call setline(1, 'xxx')
+ vsplit Xfile
+ call setline(1, 'xxxxxxxx')
+ norm! $o
+ hid
+ norm! zW
+ bwipe!
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 0fe5fc59eb..7decac2c36 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -34,7 +34,16 @@ func Test_window_cmd_cmdwin_with_vsp()
set ls&vim
endfunc
-function Test_window_cmd_wincmd_gf()
+" Test for jumping to windows
+func Test_window_jump()
+ new
+ " jumping to a window with a count greater than the max windows
+ exe "normal 4\<C-W>w"
+ call assert_equal(2, winnr())
+ only
+endfunc
+
+func Test_window_cmd_wincmd_gf()
let fname = 'test_gf.txt'
let swp_fname = '.' . fname . '.swp'
call writefile([], fname)
@@ -172,6 +181,35 @@ func Test_window_split_edit_bufnr()
%bw!
endfunc
+func Test_window_split_no_room()
+ " N horizontal windows need >= 2*N + 1 lines:
+ " - 1 line + 1 status line in each window
+ " - 1 Ex command line
+ "
+ " 2*N + 1 <= &lines
+ " N <= (lines - 1)/2
+ "
+ " Beyond that number of windows, E36: Not enough room is expected.
+ let hor_win_count = (&lines - 1)/2
+ let hor_split_count = hor_win_count - 1
+ for s in range(1, hor_split_count) | split | endfor
+ call assert_fails('split', 'E36:')
+
+ " N vertical windows need >= 2*(N - 1) + 1 columns:
+ " - 1 column + 1 separator for each window (except last window)
+ " - 1 column for the last window which does not have separator
+ "
+ " 2*(N - 1) + 1 <= &columns
+ " 2*N - 1 <= &columns
+ " N <= (&columns + 1)/2
+ let ver_win_count = (&columns + 1)/2
+ let ver_split_count = ver_win_count - 1
+ for s in range(1, ver_split_count) | vsplit | endfor
+ call assert_fails('vsplit', 'E36:')
+
+ %bw!
+endfunc
+
func Test_window_exchange()
e Xa
@@ -612,7 +650,7 @@ func Test_window_prevwin()
" reset
q
call delete('tmp.txt')
- set nohidden autoread&vim
+ set hidden&vim autoread&vim
delfunc Fun_RenewFile
endfunc
@@ -886,6 +924,155 @@ func Test_floatwin_splitmove()
bwipe
endfunc
+" Test for the :only command
+func Test_window_only()
+ new
+ set modified
+ new
+ call assert_fails('only', 'E445:')
+ only!
+ " Test for :only with a count
+ let wid = win_getid()
+ new
+ new
+ 3only
+ call assert_equal(1, winnr('$'))
+ call assert_equal(wid, win_getid())
+ call assert_fails('close', 'E444:')
+ call assert_fails('%close', 'E16:')
+endfunc
+
+" Test for errors with :wincmd
+func Test_wincmd_errors()
+ call assert_fails('wincmd g', 'E474:')
+ call assert_fails('wincmd ab', 'E474:')
+endfunc
+
+" Test for errors with :winpos
+func Test_winpos_errors()
+ throw 'Skipped: Nvim does not have :winpos'
+ if !has("gui_running") && !has('win32')
+ call assert_fails('winpos', 'E188:')
+ endif
+ call assert_fails('winpos 10', 'E466:')
+endfunc
+
+" Test for +cmd in a :split command
+func Test_split_cmd()
+ split +set\ readonly
+ call assert_equal(1, &readonly)
+ call assert_equal(2, winnr('$'))
+ close
+endfunc
+
+" Create maximum number of horizontally or vertically split windows and then
+" run commands that create a new horizontally/vertically split window
+func Run_noroom_for_newwindow_test(dir_arg)
+ let dir = (a:dir_arg == 'v') ? 'vert ' : ''
+
+ " Open as many windows as possible
+ for i in range(500)
+ try
+ exe dir . 'new'
+ catch /E36:/
+ break
+ endtry
+ endfor
+
+ call writefile(['first', 'second', 'third'], 'Xfile1')
+ call writefile([], 'Xfile2')
+ call writefile([], 'Xfile3')
+
+ " Argument list related commands
+ args Xfile1 Xfile2 Xfile3
+ next
+ for cmd in ['sargument 2', 'snext', 'sprevious', 'sNext', 'srewind',
+ \ 'sfirst', 'slast']
+ call assert_fails(dir .. cmd, 'E36:')
+ endfor
+ %argdelete
+
+ " Buffer related commands
+ set modified
+ hide enew
+ for cmd in ['sbuffer Xfile1', 'sbnext', 'sbprevious', 'sbNext', 'sbrewind',
+ \ 'sbfirst', 'sblast', 'sball', 'sbmodified', 'sunhide']
+ call assert_fails(dir .. cmd, 'E36:')
+ endfor
+
+ " Window related commands
+ for cmd in ['split', 'split Xfile2', 'new', 'new Xfile3', 'sview Xfile1',
+ \ 'sfind runtest.vim']
+ call assert_fails(dir .. cmd, 'E36:')
+ endfor
+
+ " Help
+ call assert_fails(dir .. 'help', 'E36:')
+ call assert_fails(dir .. 'helpgrep window', 'E36:')
+
+ " Command-line window
+ if a:dir_arg == 'h'
+ " Cmd-line window is always a horizontally split window
+ call assert_beeps('call feedkeys("q:\<CR>", "xt")')
+ endif
+
+ " Quickfix and location list window
+ if has('quickfix')
+ cexpr ''
+ call assert_fails(dir .. 'copen', 'E36:')
+ lexpr ''
+ call assert_fails(dir .. 'lopen', 'E36:')
+
+ " Preview window
+ call assert_fails(dir .. 'pedit Xfile2', 'E36:')
+ call setline(1, 'abc')
+ call assert_fails(dir .. 'psearch abc', 'E36:')
+ endif
+
+ " Window commands (CTRL-W ^ and CTRL-W f)
+ if a:dir_arg == 'h'
+ call assert_fails('call feedkeys("\<C-W>^", "xt")', 'E36:')
+ call setline(1, 'Xfile1')
+ call assert_fails('call feedkeys("gg\<C-W>f", "xt")', 'E36:')
+ endif
+ enew!
+
+ " Tag commands (:stag, :stselect and :stjump)
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "second\tXfile1\t2",
+ \ "third\tXfile1\t3",],
+ \ 'Xtags')
+ set tags=Xtags
+ call assert_fails(dir .. 'stag second', 'E36:')
+ call assert_fails('call feedkeys(":" .. dir .. "stselect second\n1\n", "xt")', 'E36:')
+ call assert_fails(dir .. 'stjump second', 'E36:')
+ call assert_fails(dir .. 'ptag second', 'E36:')
+ set tags&
+ call delete('Xtags')
+
+ " :isplit and :dsplit
+ call setline(1, ['#define FOO 1', 'FOO'])
+ normal 2G
+ call assert_fails(dir .. 'isplit FOO', 'E36:')
+ call assert_fails(dir .. 'dsplit FOO', 'E36:')
+
+ " terminal
+ if has('terminal')
+ call assert_fails(dir .. 'terminal', 'E36:')
+ endif
+
+ %bwipe!
+ call delete('Xfile1')
+ call delete('Xfile2')
+ call delete('Xfile3')
+ only
+endfunc
+
+func Test_split_cmds_with_no_room()
+ call Run_noroom_for_newwindow_test('h')
+ call Run_noroom_for_newwindow_test('v')
+endfunc
+
func Test_window_resize()
throw 'Skipped: Nvim supports cmdheight=0'
" Vertical :resize (absolute, relative, min and max size).
@@ -941,6 +1128,181 @@ func Test_window_resize()
%bwipe!
endfunc
+" Test for adjusting the window width when a window is closed with some
+" windows using 'winfixwidth'
+func Test_window_width_adjust()
+ only
+ " Three vertical windows. Windows 1 and 2 have 'winfixwidth' set and close
+ " window 2.
+ wincmd v
+ vert resize 10
+ set winfixwidth
+ wincmd v
+ set winfixwidth
+ wincmd c
+ call assert_inrange(10, 12, winwidth(1))
+ " Three vertical windows. Windows 2 and 3 have 'winfixwidth' set and close
+ " window 3.
+ only
+ set winfixwidth
+ wincmd v
+ vert resize 10
+ set winfixwidth
+ wincmd v
+ set nowinfixwidth
+ wincmd b
+ wincmd c
+ call assert_inrange(10, 12, winwidth(2))
+
+ new | only
+endfunc
+
+" Test for jumping to a vertical/horizontal neighbor window based on the
+" current cursor position
+func Test_window_goto_neightbor()
+ %bw!
+
+ " Vertical window movement
+
+ " create the following window layout:
+ " +--+--+
+ " |w1|w3|
+ " +--+ |
+ " |w2| |
+ " +--+--+
+ " |w4 |
+ " +-----+
+ new
+ vsplit
+ split
+ " vertically jump from w4
+ wincmd b
+ call setline(1, repeat(' ', &columns))
+ call cursor(1, 1)
+ wincmd k
+ call assert_equal(2, winnr())
+ wincmd b
+ call cursor(1, &columns)
+ redraw!
+ wincmd k
+ call assert_equal(3, winnr())
+ %bw!
+
+ " create the following window layout:
+ " +--+--+--+
+ " |w1|w2|w3|
+ " +--+--+--+
+ " |w4 |
+ " +--------+
+ new
+ vsplit
+ vsplit
+ wincmd b
+ call setline(1, repeat(' ', &columns))
+ call cursor(1, 1)
+ wincmd k
+ call assert_equal(1, winnr())
+ wincmd b
+ call cursor(1, &columns / 2)
+ redraw!
+ wincmd k
+ call assert_equal(2, winnr())
+ wincmd b
+ call cursor(1, &columns)
+ redraw!
+ wincmd k
+ call assert_equal(3, winnr())
+ %bw!
+
+ " Horizontal window movement
+
+ " create the following window layout:
+ " +--+--+--+
+ " |w1|w2|w4|
+ " +--+--+ |
+ " |w3 | |
+ " +-----+--+
+ vsplit
+ split
+ vsplit
+ 4wincmd l
+ call setline(1, repeat([' '], &lines))
+ call cursor(1, 1)
+ redraw!
+ wincmd h
+ call assert_equal(2, winnr())
+ 4wincmd l
+ call cursor(&lines, 1)
+ redraw!
+ wincmd h
+ call assert_equal(3, winnr())
+ %bw!
+
+ " create the following window layout:
+ " +--+--+
+ " |w1|w4|
+ " +--+ +
+ " |w2| |
+ " +--+ +
+ " |w3| |
+ " +--+--+
+ vsplit
+ split
+ split
+ wincmd l
+ call setline(1, repeat([' '], &lines))
+ call cursor(1, 1)
+ redraw!
+ wincmd h
+ call assert_equal(1, winnr())
+ wincmd l
+ call cursor(&lines / 2, 1)
+ redraw!
+ wincmd h
+ call assert_equal(2, winnr())
+ wincmd l
+ call cursor(&lines, 1)
+ redraw!
+ wincmd h
+ call assert_equal(3, winnr())
+ %bw!
+endfunc
+
+" Test for an autocmd closing the destination window when jumping from one
+" window to another.
+func Test_close_dest_window()
+ split
+ edit Xfile
+
+ " Test for BufLeave
+ augroup T1
+ au!
+ au BufLeave Xfile $wincmd c
+ augroup END
+ wincmd b
+ call assert_equal(1, winnr('$'))
+ call assert_equal('Xfile', @%)
+ augroup T1
+ au!
+ augroup END
+
+ " Test for WinLeave
+ new
+ wincmd p
+ augroup T1
+ au!
+ au WinLeave * 1wincmd c
+ augroup END
+ wincmd t
+ call assert_equal(1, winnr('$'))
+ call assert_equal('Xfile', @%)
+ augroup T1
+ au!
+ augroup END
+ augroup! T1
+ %bw!
+endfunc
+
func Test_win_move_separator()
edit a
leftabove vsplit b
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index b42665c9b5..bfbba1f793 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -206,6 +206,8 @@ func Test_write_errors()
call assert_fails('1,2write', 'E140:')
close!
+ call assert_fails('w > Xtest', 'E494:')
+
" Try to overwrite a directory
if has('unix')
call mkdir('Xdir1')
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 9207ebe73b..69b687e44f 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -110,9 +110,13 @@ static void ga_concat_shorten_esc(garray_T *gap, const char_u *str)
/// Fill "gap" with information about an assert error.
static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str,
- typval_T *exp_tv, typval_T *got_tv, assert_type_T atype)
+ typval_T *exp_tv_arg, typval_T *got_tv_arg, assert_type_T atype)
{
char_u *tofree;
+ typval_T *exp_tv = exp_tv_arg;
+ typval_T *got_tv = got_tv_arg;
+ bool did_copy = false;
+ int omitted = 0;
if (opt_msg_tv->v_type != VAR_UNKNOWN) {
tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL);
@@ -130,6 +134,55 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s
}
if (exp_str == NULL) {
+ // When comparing dictionaries, drop the items that are equal, so that
+ // it's a lot easier to see what differs.
+ if (atype != ASSERT_NOTEQUAL
+ && exp_tv->v_type == VAR_DICT && got_tv->v_type == VAR_DICT
+ && exp_tv->vval.v_dict != NULL && got_tv->vval.v_dict != NULL) {
+ dict_T *exp_d = exp_tv->vval.v_dict;
+ dict_T *got_d = got_tv->vval.v_dict;
+
+ did_copy = true;
+ exp_tv->vval.v_dict = tv_dict_alloc();
+ got_tv->vval.v_dict = tv_dict_alloc();
+
+ int todo = (int)exp_d->dv_hashtab.ht_used;
+ for (const hashitem_T *hi = exp_d->dv_hashtab.ht_array; todo > 0; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ dictitem_T *item2 = tv_dict_find(got_d, (const char *)hi->hi_key, -1);
+ if (item2 == NULL
+ || !tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &item2->di_tv, false, false)) {
+ // item of exp_d not present in got_d or values differ.
+ const size_t key_len = STRLEN(hi->hi_key);
+ tv_dict_add_tv(exp_tv->vval.v_dict, (const char *)hi->hi_key, key_len,
+ &TV_DICT_HI2DI(hi)->di_tv);
+ if (item2 != NULL) {
+ tv_dict_add_tv(got_tv->vval.v_dict, (const char *)hi->hi_key, key_len,
+ &item2->di_tv);
+ }
+ } else {
+ omitted++;
+ }
+ todo--;
+ }
+ }
+
+ // Add items only present in got_d.
+ todo = (int)got_d->dv_hashtab.ht_used;
+ for (const hashitem_T *hi = got_d->dv_hashtab.ht_array; todo > 0; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ dictitem_T *item2 = tv_dict_find(exp_d, (const char *)hi->hi_key, -1);
+ if (item2 == NULL) {
+ // item of got_d not present in exp_d
+ const size_t key_len = STRLEN(hi->hi_key);
+ tv_dict_add_tv(got_tv->vval.v_dict, (const char *)hi->hi_key, key_len,
+ &TV_DICT_HI2DI(hi)->di_tv);
+ }
+ todo--;
+ }
+ }
+ }
+
tofree = (char_u *)encode_tv2string(exp_tv, NULL);
ga_concat_shorten_esc(gap, tofree);
xfree(tofree);
@@ -148,6 +201,18 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s
tofree = (char_u *)encode_tv2string(got_tv, NULL);
ga_concat_shorten_esc(gap, tofree);
xfree(tofree);
+
+ if (omitted != 0) {
+ char buf[100];
+ vim_snprintf(buf, sizeof(buf), " - %d equal item%s omitted", omitted,
+ omitted == 1 ? "" : "s");
+ ga_concat(gap, buf);
+ }
+ }
+
+ if (did_copy) {
+ tv_clear(exp_tv);
+ tv_clear(got_tv);
}
}
@@ -405,15 +470,15 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *const cmd = tv_get_string_chk(&argvars[0]);
garray_T ga;
int save_trylevel = trylevel;
+ const int called_emsg_before = called_emsg;
// trylevel must be zero for a ":throw" command to be considered failed
trylevel = 0;
- called_emsg = false;
suppress_errthrow = true;
emsg_silent = true;
do_cmdline_cmd(cmd);
- if (!called_emsg) {
+ if (called_emsg == called_emsg_before) {
prepare_assert_error(&ga);
ga_concat(&ga, "command did not fail: ");
assert_append_cmd_or_arg(&ga, argvars, cmd);
@@ -438,7 +503,6 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
trylevel = save_trylevel;
- called_emsg = false;
suppress_errthrow = false;
emsg_silent = false;
emsg_on_display = false;
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 3e24e892b8..61a59bcf06 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -23,6 +23,91 @@
#define KEY_BUFFER_SIZE 0xfff
+static const struct kitty_key_map_entry {
+ KittyKey key;
+ const char *name;
+} kitty_key_map_entry[] = {
+ { KITTY_KEY_ESCAPE, "Esc" },
+ { KITTY_KEY_ENTER, "CR" },
+ { KITTY_KEY_TAB, "Tab" },
+ { KITTY_KEY_BACKSPACE, "BS" },
+ { KITTY_KEY_INSERT, "Insert" },
+ { KITTY_KEY_DELETE, "Del" },
+ { KITTY_KEY_LEFT, "Left" },
+ { KITTY_KEY_RIGHT, "Right" },
+ { KITTY_KEY_UP, "Up" },
+ { KITTY_KEY_DOWN, "Down" },
+ { KITTY_KEY_PAGE_UP, "PageUp" },
+ { KITTY_KEY_PAGE_DOWN, "PageDown" },
+ { KITTY_KEY_HOME, "Home" },
+ { KITTY_KEY_END, "End" },
+ { KITTY_KEY_F1, "F1" },
+ { KITTY_KEY_F2, "F2" },
+ { KITTY_KEY_F3, "F3" },
+ { KITTY_KEY_F4, "F4" },
+ { KITTY_KEY_F5, "F5" },
+ { KITTY_KEY_F6, "F6" },
+ { KITTY_KEY_F7, "F7" },
+ { KITTY_KEY_F8, "F8" },
+ { KITTY_KEY_F9, "F9" },
+ { KITTY_KEY_F10, "F10" },
+ { KITTY_KEY_F11, "F11" },
+ { KITTY_KEY_F12, "F12" },
+ { KITTY_KEY_F13, "F13" },
+ { KITTY_KEY_F14, "F14" },
+ { KITTY_KEY_F15, "F15" },
+ { KITTY_KEY_F16, "F16" },
+ { KITTY_KEY_F17, "F17" },
+ { KITTY_KEY_F18, "F18" },
+ { KITTY_KEY_F19, "F19" },
+ { KITTY_KEY_F20, "F20" },
+ { KITTY_KEY_F21, "F21" },
+ { KITTY_KEY_F22, "F22" },
+ { KITTY_KEY_F23, "F23" },
+ { KITTY_KEY_F24, "F24" },
+ { KITTY_KEY_F25, "F25" },
+ { KITTY_KEY_F26, "F26" },
+ { KITTY_KEY_F27, "F27" },
+ { KITTY_KEY_F28, "F28" },
+ { KITTY_KEY_F29, "F29" },
+ { KITTY_KEY_F30, "F30" },
+ { KITTY_KEY_F31, "F31" },
+ { KITTY_KEY_F32, "F32" },
+ { KITTY_KEY_F33, "F33" },
+ { KITTY_KEY_F34, "F34" },
+ { KITTY_KEY_F35, "F35" },
+ { KITTY_KEY_KP_0, "k0" },
+ { KITTY_KEY_KP_1, "k1" },
+ { KITTY_KEY_KP_2, "k2" },
+ { KITTY_KEY_KP_3, "k3" },
+ { KITTY_KEY_KP_4, "k4" },
+ { KITTY_KEY_KP_5, "k5" },
+ { KITTY_KEY_KP_6, "k6" },
+ { KITTY_KEY_KP_7, "k7" },
+ { KITTY_KEY_KP_8, "k8" },
+ { KITTY_KEY_KP_9, "k9" },
+ { KITTY_KEY_KP_DECIMAL, "kPoint" },
+ { KITTY_KEY_KP_DIVIDE, "kDivide" },
+ { KITTY_KEY_KP_MULTIPLY, "kMultiply" },
+ { KITTY_KEY_KP_SUBTRACT, "kMinus" },
+ { KITTY_KEY_KP_ADD, "kPlus" },
+ { KITTY_KEY_KP_ENTER, "kEnter" },
+ { KITTY_KEY_KP_EQUAL, "kEqual" },
+ { KITTY_KEY_KP_LEFT, "kLeft" },
+ { KITTY_KEY_KP_RIGHT, "kRight" },
+ { KITTY_KEY_KP_UP, "kUp" },
+ { KITTY_KEY_KP_DOWN, "kDown" },
+ { KITTY_KEY_KP_PAGE_UP, "kPageUp" },
+ { KITTY_KEY_KP_PAGE_DOWN, "kPageDown" },
+ { KITTY_KEY_KP_HOME, "kHome" },
+ { KITTY_KEY_KP_END, "kEnd" },
+ { KITTY_KEY_KP_INSERT, "kInsert" },
+ { KITTY_KEY_KP_DELETE, "kDel" },
+ { KITTY_KEY_KP_BEGIN, "kOrigin" },
+};
+
+static Map(KittyKey, cstr_t) kitty_key_map = MAP_INIT;
+
#ifndef UNIT_TESTING
typedef enum {
kIncomplete = -1,
@@ -50,6 +135,11 @@ void tinput_init(TermInput *input, Loop *loop)
uv_mutex_init(&input->key_buffer_mutex);
uv_cond_init(&input->key_buffer_cond);
+ for (size_t i = 0; i < ARRAY_SIZE(kitty_key_map_entry); i++) {
+ map_put(KittyKey, cstr_t)(&kitty_key_map, kitty_key_map_entry[i].key,
+ kitty_key_map_entry[i].name);
+ }
+
// If stdin is not a pty, switch to stderr. For cases like:
// echo q | nvim -es
// ls *.md | xargs nvim
@@ -89,6 +179,7 @@ void tinput_init(TermInput *input, Loop *loop)
void tinput_destroy(TermInput *input)
{
+ map_destroy(KittyKey, cstr_t)(&kitty_key_map);
rbuffer_free(input->key_buffer);
uv_mutex_destroy(&input->key_buffer_mutex);
uv_cond_destroy(&input->key_buffer_cond);
@@ -206,19 +297,46 @@ static void tinput_enqueue(TermInput *input, char *buf, size_t size)
rbuffer_write(input->key_buffer, buf, size);
}
+static void handle_kitty_key_protocol(TermInput *input, TermKeyKey *key)
+{
+ const char *name = map_get(KittyKey, cstr_t)(&kitty_key_map, (KittyKey)key->code.codepoint);
+ if (name) {
+ char buf[64];
+ size_t len = 0;
+ buf[len++] = '<';
+ if (key->modifiers & TERMKEY_KEYMOD_SHIFT) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "S-");
+ }
+ if (key->modifiers & TERMKEY_KEYMOD_ALT) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "A-");
+ }
+ if (key->modifiers & TERMKEY_KEYMOD_CTRL) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "C-");
+ }
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "%s>", name);
+ tinput_enqueue(input, buf, len);
+ }
+}
+
static void forward_simple_utf8(TermInput *input, TermKeyKey *key)
{
size_t len = 0;
char buf[64];
char *ptr = key->utf8;
- while (*ptr) {
- if (*ptr == '<') {
- len += (size_t)snprintf(buf + len, sizeof(buf) - len, "<lt>");
- } else {
- buf[len++] = *ptr;
+ if (key->code.codepoint >= 0xE000 && key->code.codepoint <= 0xF8FF
+ && map_has(KittyKey, cstr_t)(&kitty_key_map, (KittyKey)key->code.codepoint)) {
+ handle_kitty_key_protocol(input, key);
+ return;
+ } else {
+ while (*ptr) {
+ if (*ptr == '<') {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "<lt>");
+ } else {
+ buf[len++] = *ptr;
+ }
+ ptr++;
}
- ptr++;
}
tinput_enqueue(input, buf, len);
@@ -236,19 +354,26 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key)
len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
} else {
assert(key->modifiers);
- // Termkey doesn't include the S- modifier for ASCII characters (e.g.,
- // ctrl-shift-l is <C-L> instead of <C-S-L>. Vim, on the other hand,
- // treats <C-L> and <C-l> the same, requiring the S- modifier.
- len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
- if ((key->modifiers & TERMKEY_KEYMOD_CTRL)
- && !(key->modifiers & TERMKEY_KEYMOD_SHIFT)
- && ASCII_ISUPPER(key->code.codepoint)) {
- assert(len <= 62);
- // Make room for the S-
- memmove(buf + 3, buf + 1, len - 1);
- buf[1] = 'S';
- buf[2] = '-';
- len += 2;
+ if (key->code.codepoint >= 0xE000 && key->code.codepoint <= 0xF8FF
+ && map_has(KittyKey, cstr_t)(&kitty_key_map,
+ (KittyKey)key->code.codepoint)) {
+ handle_kitty_key_protocol(input, key);
+ return;
+ } else {
+ // Termkey doesn't include the S- modifier for ASCII characters (e.g.,
+ // ctrl-shift-l is <C-L> instead of <C-S-L>. Vim, on the other hand,
+ // treats <C-L> and <C-l> the same, requiring the S- modifier.
+ len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
+ if ((key->modifiers & TERMKEY_KEYMOD_CTRL)
+ && !(key->modifiers & TERMKEY_KEYMOD_SHIFT)
+ && ASCII_ISUPPER(key->code.codepoint)) {
+ assert(len <= 62);
+ // Make room for the S-
+ memmove(buf + 3, buf + 1, len - 1);
+ buf[1] = 'S';
+ buf[2] = '-';
+ len += 2;
+ }
}
}
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
index 84daf40744..51df57938c 100644
--- a/src/nvim/tui/input.h
+++ b/src/nvim/tui/input.h
@@ -6,6 +6,7 @@
#include "nvim/event/stream.h"
#include "nvim/event/time.h"
+#include "nvim/tui/input_defs.h"
#include "nvim/tui/tui.h"
typedef enum {
diff --git a/src/nvim/tui/input_defs.h b/src/nvim/tui/input_defs.h
new file mode 100644
index 0000000000..846cf45350
--- /dev/null
+++ b/src/nvim/tui/input_defs.h
@@ -0,0 +1,118 @@
+#ifndef NVIM_TUI_INPUT_DEFS_H
+#define NVIM_TUI_INPUT_DEFS_H
+
+typedef enum {
+ KITTY_KEY_ESCAPE = 57344,
+ KITTY_KEY_ENTER = 57345,
+ KITTY_KEY_TAB = 57346,
+ KITTY_KEY_BACKSPACE = 57347,
+ KITTY_KEY_INSERT = 57348,
+ KITTY_KEY_DELETE = 57349,
+ KITTY_KEY_LEFT = 57350,
+ KITTY_KEY_RIGHT = 57351,
+ KITTY_KEY_UP = 57352,
+ KITTY_KEY_DOWN = 57353,
+ KITTY_KEY_PAGE_UP = 57354,
+ KITTY_KEY_PAGE_DOWN = 57355,
+ KITTY_KEY_HOME = 57356,
+ KITTY_KEY_END = 57357,
+ KITTY_KEY_CAPS_LOCK = 57358,
+ KITTY_KEY_SCROLL_LOCK = 57359,
+ KITTY_KEY_NUM_LOCK = 57360,
+ KITTY_KEY_PRINT_SCREEN = 57361,
+ KITTY_KEY_PAUSE = 57362,
+ KITTY_KEY_MENU = 57363,
+ KITTY_KEY_F1 = 57364,
+ KITTY_KEY_F2 = 57365,
+ KITTY_KEY_F3 = 57366,
+ KITTY_KEY_F4 = 57367,
+ KITTY_KEY_F5 = 57368,
+ KITTY_KEY_F6 = 57369,
+ KITTY_KEY_F7 = 57370,
+ KITTY_KEY_F8 = 57371,
+ KITTY_KEY_F9 = 57372,
+ KITTY_KEY_F10 = 57373,
+ KITTY_KEY_F11 = 57374,
+ KITTY_KEY_F12 = 57375,
+ KITTY_KEY_F13 = 57376,
+ KITTY_KEY_F14 = 57377,
+ KITTY_KEY_F15 = 57378,
+ KITTY_KEY_F16 = 57379,
+ KITTY_KEY_F17 = 57380,
+ KITTY_KEY_F18 = 57381,
+ KITTY_KEY_F19 = 57382,
+ KITTY_KEY_F20 = 57383,
+ KITTY_KEY_F21 = 57384,
+ KITTY_KEY_F22 = 57385,
+ KITTY_KEY_F23 = 57386,
+ KITTY_KEY_F24 = 57387,
+ KITTY_KEY_F25 = 57388,
+ KITTY_KEY_F26 = 57389,
+ KITTY_KEY_F27 = 57390,
+ KITTY_KEY_F28 = 57391,
+ KITTY_KEY_F29 = 57392,
+ KITTY_KEY_F30 = 57393,
+ KITTY_KEY_F31 = 57394,
+ KITTY_KEY_F32 = 57395,
+ KITTY_KEY_F33 = 57396,
+ KITTY_KEY_F34 = 57397,
+ KITTY_KEY_F35 = 57398,
+ KITTY_KEY_KP_0 = 57399,
+ KITTY_KEY_KP_1 = 57400,
+ KITTY_KEY_KP_2 = 57401,
+ KITTY_KEY_KP_3 = 57402,
+ KITTY_KEY_KP_4 = 57403,
+ KITTY_KEY_KP_5 = 57404,
+ KITTY_KEY_KP_6 = 57405,
+ KITTY_KEY_KP_7 = 57406,
+ KITTY_KEY_KP_8 = 57407,
+ KITTY_KEY_KP_9 = 57408,
+ KITTY_KEY_KP_DECIMAL = 57409,
+ KITTY_KEY_KP_DIVIDE = 57410,
+ KITTY_KEY_KP_MULTIPLY = 57411,
+ KITTY_KEY_KP_SUBTRACT = 57412,
+ KITTY_KEY_KP_ADD = 57413,
+ KITTY_KEY_KP_ENTER = 57414,
+ KITTY_KEY_KP_EQUAL = 57415,
+ KITTY_KEY_KP_SEPARATOR = 57416,
+ KITTY_KEY_KP_LEFT = 57417,
+ KITTY_KEY_KP_RIGHT = 57418,
+ KITTY_KEY_KP_UP = 57419,
+ KITTY_KEY_KP_DOWN = 57420,
+ KITTY_KEY_KP_PAGE_UP = 57421,
+ KITTY_KEY_KP_PAGE_DOWN = 57422,
+ KITTY_KEY_KP_HOME = 57423,
+ KITTY_KEY_KP_END = 57424,
+ KITTY_KEY_KP_INSERT = 57425,
+ KITTY_KEY_KP_DELETE = 57426,
+ KITTY_KEY_KP_BEGIN = 57427,
+ KITTY_KEY_MEDIA_PLAY = 57428,
+ KITTY_KEY_MEDIA_PAUSE = 57429,
+ KITTY_KEY_MEDIA_PLAY_PAUSE = 57430,
+ KITTY_KEY_MEDIA_REVERSE = 57431,
+ KITTY_KEY_MEDIA_STOP = 57432,
+ KITTY_KEY_MEDIA_FAST_FORWARD = 57433,
+ KITTY_KEY_MEDIA_REWIND = 57434,
+ KITTY_KEY_MEDIA_TRACK_NEXT = 57435,
+ KITTY_KEY_MEDIA_TRACK_PREVIOUS = 57436,
+ KITTY_KEY_MEDIA_RECORD = 57437,
+ KITTY_KEY_LOWER_VOLUME = 57438,
+ KITTY_KEY_RAISE_VOLUME = 57439,
+ KITTY_KEY_MUTE_VOLUME = 57440,
+ KITTY_KEY_LEFT_SHIFT = 57441,
+ KITTY_KEY_LEFT_CONTROL = 57442,
+ KITTY_KEY_LEFT_ALT = 57443,
+ KITTY_KEY_LEFT_SUPER = 57444,
+ KITTY_KEY_LEFT_HYPER = 57445,
+ KITTY_KEY_LEFT_META = 57446,
+ KITTY_KEY_RIGHT_SHIFT = 57447,
+ KITTY_KEY_RIGHT_CONTROL = 57448,
+ KITTY_KEY_RIGHT_ALT = 57449,
+ KITTY_KEY_RIGHT_SUPER = 57450,
+ KITTY_KEY_RIGHT_HYPER = 57451,
+ KITTY_KEY_RIGHT_META = 57452,
+ KITTY_KEY_ISO_LEVEL3_SHIFT = 57453,
+ KITTY_KEY_ISO_LEVEL5_SHIFT = 57454,
+} KittyKey;
+
+#endif // NVIM_TUI_INPUT_DEFS_H
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index f492792b20..e2289eb9ce 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -137,8 +137,7 @@ struct TUIData {
char *space_buf;
};
-static bool volatile got_winch = false;
-static bool did_user_set_dimensions = false;
+static int got_winch = 0;
static bool cursor_style_enabled = false;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -535,7 +534,7 @@ static void sigcont_cb(SignalWatcher *watcher, int signum, void *data)
static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data)
{
- got_winch = true;
+ got_winch++;
UI *ui = data;
if (tui_is_stopped(ui)) {
return;
@@ -987,7 +986,7 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height)
r->right = MIN(r->right, grid->width);
}
- if (!got_winch && (!data->is_starting || did_user_set_dimensions)) {
+ if (!got_winch && !data->is_starting) {
// Resize the _host_ terminal.
UNIBI_SET_NUM_VAR(data->params[0], (int)height);
UNIBI_SET_NUM_VAR(data->params[1], (int)width);
@@ -997,7 +996,7 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height)
reset_scroll_region(ui, ui->width == grid->width);
}
} else { // Already handled the SIGWINCH signal; avoid double-resize.
- got_winch = false;
+ got_winch = got_winch > 0 ? got_winch - 1 : 0;
grid->row = -1;
}
}
@@ -1504,23 +1503,13 @@ static void tui_guess_size(UI *ui)
TUIData *data = ui->data;
int width = 0, height = 0;
- // 1 - look for non-default 'columns' and 'lines' options during startup
- if (data->is_starting && (Columns != DFLT_COLS || Rows != DFLT_ROWS)) {
- did_user_set_dimensions = true;
- assert(Columns >= 0);
- assert(Rows >= 0);
- width = Columns;
- height = Rows;
- goto end;
- }
-
- // 2 - try from a system call(ioctl/TIOCGWINSZ on unix)
+ // 1 - try from a system call(ioctl/TIOCGWINSZ on unix)
if (data->out_isatty
&& !uv_tty_get_winsize(&data->output_handle.tty, &width, &height)) {
goto end;
}
- // 3 - use $LINES/$COLUMNS if available
+ // 2 - use $LINES/$COLUMNS if available
const char *val;
int advance;
if ((val = os_getenv("LINES"))
@@ -1530,7 +1519,7 @@ static void tui_guess_size(UI *ui)
goto end;
}
- // 4 - read from terminfo if available
+ // 3 - read from terminfo if available
height = unibi_get_num(data->ut, unibi_lines);
width = unibi_get_num(data->ut, unibi_columns);
@@ -1653,6 +1642,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
bool xterm = terminfo_is_term_family(term, "xterm")
// Treat Terminal.app as generic xterm-like, for now.
|| nsterm;
+ bool hterm = terminfo_is_term_family(term, "hterm");
bool kitty = terminfo_is_term_family(term, "xterm-kitty");
bool linuxvt = terminfo_is_term_family(term, "linux");
bool bsdvt = terminfo_is_bsd_console(term);
@@ -1716,7 +1706,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
unibi_set_bool(ut, unibi_back_color_erase, false);
}
- if (xterm) {
+ if (xterm || hterm) {
// Termit, LXTerminal, GTKTerm2, GNOME Terminal, MATE Terminal, roxterm,
// and EvilVTE falsely claim to be xterm and do not support important xterm
// control sequences that we use. In an ideal world, these would have
@@ -1725,9 +1715,13 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
// treatable as xterm.
// 2017-04 terminfo.src lacks these. Xterm-likes have them.
- unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;");
- unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
- unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr");
+ if (!hterm) {
+ // hterm doesn't have a status line.
+ unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;");
+ unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
+ // TODO(aktau): patch this in when DECSTBM is fixed (https://crbug.com/1298796)
+ unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr");
+ }
unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m");
@@ -1738,6 +1732,9 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds");
} else {
// Fix things advertised via TERM=xterm, for non-xterm.
+ //
+ // TODO(aktau): stop patching this out for hterm when it gains support
+ // (https://crbug.com/1175065).
if (unibi_get_str(ut, unibi_set_lr_margin)) {
ILOG("Disabling smglr with TERM=xterm for non-xterm.");
unibi_set_str(ut, unibi_set_lr_margin, NULL);
@@ -1886,6 +1883,8 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
&& ((xterm && !vte_version) // anything claiming xterm compat
// per MinTTY 0.4.3-1 release notes from 2009
|| putty
+ // per https://chromium.googlesource.com/apps/libapps/+/a5fb83c190aa9d74f4a9bca233dac6be2664e9e9/hterm/doc/ControlSequences.md
+ || hterm
// per https://bugzilla.gnome.org/show_bug.cgi?id=720821
|| (vte_version >= 3900)
|| (konsolev >= 180770) // #9364
@@ -1970,6 +1969,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
bool xterm = terminfo_is_term_family(term, "xterm")
// Treat Terminal.app as generic xterm-like, for now.
|| nsterm;
+ bool hterm = terminfo_is_term_family(term, "hterm");
bool bsdvt = terminfo_is_bsd_console(term);
bool dtterm = terminfo_is_term_family(term, "dtterm");
bool rxvt = terminfo_is_term_family(term, "rxvt");
@@ -1999,7 +1999,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
"ext.resize_screen",
"\x1b[8;%p1%d;%p2%dt");
}
- if (putty || xterm || rxvt) {
+ if (putty || xterm || hterm || rxvt) {
data->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut,
"ext.reset_scroll_region",
"\x1b[r");
@@ -2048,22 +2048,27 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
}
}
- if (iterm || iterm_pretending_xterm) {
- // FIXME: Bypassing tmux like this affects the cursor colour globally, in
- // all panes, which is not particularly desirable. A better approach
- // would use a tmux control sequence and an extra if(screen) test.
- data->unibi_ext.set_cursor_color =
- (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\"));
- } else if ((xterm || rxvt || tmux || alacritty)
- && (vte_version == 0 || vte_version >= 3900)) {
- // Supported in urxvt, newer VTE.
- data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color",
- "\033]12;#%p1%06x\007");
+ data->unibi_ext.set_cursor_color = unibi_find_ext_str(ut, "Cs");
+ if (-1 == data->unibi_ext.set_cursor_color) {
+ if (iterm || iterm_pretending_xterm) {
+ // FIXME: Bypassing tmux like this affects the cursor colour globally, in
+ // all panes, which is not particularly desirable. A better approach
+ // would use a tmux control sequence and an extra if(screen) test.
+ data->unibi_ext.set_cursor_color =
+ (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\"));
+ } else if ((xterm || hterm || rxvt || tmux || alacritty)
+ && (vte_version == 0 || vte_version >= 3900)) {
+ // Supported in urxvt, newer VTE.
+ data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color",
+ "\033]12;#%p1%06x\007");
+ }
}
-
if (-1 != data->unibi_ext.set_cursor_color) {
- data->unibi_ext.reset_cursor_color = (int)unibi_add_ext_str(ut, "ext.reset_cursor_color",
- "\x1b]112\x07");
+ data->unibi_ext.reset_cursor_color = unibi_find_ext_str(ut, "Cr");
+ if (-1 == data->unibi_ext.reset_cursor_color) {
+ data->unibi_ext.reset_cursor_color = (int)unibi_add_ext_str(ut, "ext.reset_cursor_color",
+ "\x1b]112\x07");
+ }
}
data->unibi_ext.save_title = (int)unibi_add_ext_str(ut, "ext.save_title", "\x1b[22;0t");
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 078cb6a210..8324db37c6 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -89,6 +89,7 @@
#include "nvim/change.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
+#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
@@ -297,7 +298,7 @@ bool undo_allowed(buf_T *buf)
// Don't allow changes in the buffer while editing the cmdline. The
// caller of getcmdline() may get confused.
if (textlock != 0) {
- emsg(_(e_secure));
+ emsg(_(e_textlock));
return false;
}
@@ -693,8 +694,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
// When not reading use the first directory that exists or ".".
dirp = (char *)p_udir;
while (*dirp != NUL) {
- size_t dir_len = copy_option_part((char_u **)&dirp, (char_u *)dir_name,
- MAXPATHL, ",");
+ size_t dir_len = copy_option_part(&dirp, dir_name, MAXPATHL, ",");
if (dir_len == 1 && dir_name[0] == '.') {
// Use same directory as the ffname,
// "dir/name" -> "dir/.name.un~"
@@ -1186,7 +1186,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
bufinfo_T bi;
if (name == NULL) {
- file_name = u_get_undo_file_name((char *)buf->b_ffname, false);
+ file_name = u_get_undo_file_name(buf->b_ffname, false);
if (file_name == NULL) {
if (p_verbose > 0) {
verbose_enter();
@@ -1291,7 +1291,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
FileInfo file_info_old;
FileInfo file_info_new;
if (buf->b_ffname != NULL
- && os_fileinfo((char *)buf->b_ffname, &file_info_old)
+ && os_fileinfo(buf->b_ffname, &file_info_old)
&& os_fileinfo(file_name, &file_info_new)
&& file_info_old.stat.st_gid != file_info_new.stat.st_gid
&& os_fchown(fd, (uv_uid_t)-1, (uv_gid_t)file_info_old.stat.st_gid)) {
@@ -1370,10 +1370,8 @@ write_error:
#ifdef HAVE_ACL
if (buf->b_ffname != NULL) {
- vim_acl_T acl;
-
// For systems that support ACL: get the ACL from the original file.
- acl = mch_get_acl(buf->b_ffname);
+ vim_acl_T acl = mch_get_acl((char_u *)buf->b_ffname);
mch_set_acl((char_u *)file_name, acl);
mch_free_acl(acl);
}
@@ -1398,7 +1396,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
char *file_name;
if (name == NULL) {
- file_name = u_get_undo_file_name((char *)curbuf->b_ffname, true);
+ file_name = u_get_undo_file_name(curbuf->b_ffname, true);
if (file_name == NULL) {
return;
}
@@ -1957,6 +1955,11 @@ void undo_time(long step, bool sec, bool file, bool absolute)
bool above = false;
bool did_undo = true;
+ if (text_locked()) {
+ text_locked_msg();
+ return;
+ }
+
// First make sure the current undoable change is synced.
if (curbuf->b_u_synced == false) {
u_sync(true);
diff --git a/src/nvim/version.c b/src/nvim/version.c
index a70778fdd9..3ffae6592c 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -332,7 +332,7 @@ static const int included_patches[] = {
1591,
1590,
1589,
- // 1588,
+ 1588,
1587,
1586,
1585,
@@ -346,11 +346,11 @@ static const int included_patches[] = {
1577,
1576,
1575,
- // 1574,
+ 1574,
1573,
1572,
1571,
- // 1570,
+ 1570,
1569,
1568,
1567,
@@ -362,7 +362,7 @@ static const int included_patches[] = {
1561,
1560,
1559,
- // 1558,
+ 1558,
1557,
1556,
1555,
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 9b9a2126ff..06231150d5 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -331,6 +331,7 @@ newwindow:
// move window to new tab page
case 'T':
+ CHECK_CMDWIN;
if (one_window(curwin)) {
msg(_(m_onlyone));
} else {
@@ -783,8 +784,8 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
}
if (!ui_has(kUIMultigrid)) {
- wp->w_height = MIN(wp->w_height, Rows - 1 - win_extra_height(wp));
- wp->w_width = MIN(wp->w_width, Columns - win_extra_width(wp));
+ wp->w_height = MIN(wp->w_height, Rows - 1 - win_border_height(wp));
+ wp->w_width = MIN(wp->w_width, Columns - win_border_width(wp));
}
win_set_inner_size(wp);
@@ -1266,8 +1267,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
} else if (wp->w_floating) {
new_frame(wp);
wp->w_floating = false;
- // non-floating window doesn't store float config.
+ // non-floating window doesn't store float config or have a border.
wp->w_float_config = FLOAT_CONFIG_INIT;
+ memset(wp->w_border_adj, 0, sizeof(wp->w_border_adj));
}
/*
@@ -1554,9 +1556,9 @@ static void win_init(win_T *newp, win_T *oldp, int flags)
copy_loclist_stack(oldp, newp);
}
newp->w_localdir = (oldp->w_localdir == NULL)
- ? NULL : vim_strsave(oldp->w_localdir);
+ ? NULL : xstrdup(oldp->w_localdir);
newp->w_prevdir = (oldp->w_prevdir == NULL)
- ? NULL : vim_strsave(oldp->w_prevdir);
+ ? NULL : xstrdup(oldp->w_prevdir);
// copy tagstack and folds
for (i = 0; i < oldp->w_tagstacklen; i++) {
@@ -1940,7 +1942,7 @@ static void win_totop(int size, int flags)
} else {
// No longer a float, a non-multigrid UI shouldn't draw it as such
ui_call_win_hide(curwin->w_grid_alloc.handle);
- win_free_grid(curwin, false);
+ win_free_grid(curwin, true);
}
} else {
// Remove the window and frame from the tree of frames.
@@ -1980,7 +1982,7 @@ void win_move_after(win_T *win1, win_T *win2)
return;
}
- // may need move the status line, window bar, horizontal or vertical separator of the last
+ // may need to move the status line, window bar, horizontal or vertical separator of the last
// window
if (win1 == lastwin) {
height = win1->w_prev->w_status_height;
@@ -2741,6 +2743,8 @@ int win_close(win_T *win, bool free_buf, bool force)
* to be the last one left, return now.
*/
if (wp->w_buffer != curbuf) {
+ reset_VIsual_and_resel(); // stop Visual mode
+
other_buffer = true;
win->w_closing = true;
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf);
@@ -4074,7 +4078,7 @@ int win_new_tabpage(int after, char_u *filename)
}
newtp->tp_localdir = old_curtab->tp_localdir
- ? vim_strsave(old_curtab->tp_localdir) : NULL;
+ ? xstrdup(old_curtab->tp_localdir) : NULL;
curtab = newtp;
@@ -4569,12 +4573,8 @@ void win_goto(win_T *wp)
{
win_T *owp = curwin;
- if (text_locked()) {
+ if (text_or_buf_locked()) {
beep_flush();
- text_locked_msg();
- return;
- }
- if (curbuf_locked()) {
return;
}
@@ -4897,8 +4897,7 @@ static void win_enter_ext(win_T *const wp, const int flags)
void fix_current_dir(void)
{
// New directory is either the local directory of the window, tab or NULL.
- char *new_dir = (char *)(curwin->w_localdir
- ? curwin->w_localdir : curtab->tp_localdir);
+ char *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->tp_localdir;
char cwd[MAXPATHL];
if (os_dirname((char_u *)cwd, MAXPATHL) != OK) {
cwd[0] = NUL;
@@ -5181,8 +5180,7 @@ void win_free_grid(win_T *wp, bool reinit)
}
grid_free(&wp->w_grid_alloc);
if (reinit) {
- // if a float is turned into a split and back into a float, the grid
- // data structure will be reused
+ // if a float is turned into a split, the grid data structure will be reused
memset(&wp->w_grid_alloc, 0, sizeof(wp->w_grid_alloc));
}
}
@@ -5560,9 +5558,10 @@ static void frame_setheight(frame_T *curfrp, int height)
if (curfrp->fr_parent == NULL) {
// topframe: can only change the command line
if (height > ROWS_AVAIL) {
- // If height is greater than the available space, try to create space for the frame by
- // reducing 'cmdheight' if possible, while making sure `cmdheight` doesn't go below 1.
- height = MIN(ROWS_AVAIL + (p_ch - 1), height);
+ // If height is greater than the available space, try to create space for
+ // the frame by reducing 'cmdheight' if possible, while making sure
+ // `cmdheight` doesn't go below 1.
+ height = MIN((p_ch > 0 ? ROWS_AVAIL + (p_ch - 1) : ROWS_AVAIL), height);
}
if (height > 0) {
frame_new_height(curfrp, height, false, false);
@@ -6332,18 +6331,18 @@ void win_set_inner_size(win_T *wp)
terminal_check_size(wp->w_buffer->terminal);
}
- wp->w_height_outer = (wp->w_height_inner + win_extra_height(wp));
- wp->w_width_outer = (wp->w_width_inner + win_extra_width(wp));
+ wp->w_height_outer = (wp->w_height_inner + win_border_height(wp) + wp->w_winbar_height);
+ wp->w_width_outer = (wp->w_width_inner + win_border_width(wp));
wp->w_winrow_off = wp->w_border_adj[0] + wp->w_winbar_height;
wp->w_wincol_off = wp->w_border_adj[3];
}
-static int win_extra_height(win_T *wp)
+static int win_border_height(win_T *wp)
{
- return wp->w_border_adj[0] + wp->w_border_adj[2] + wp->w_winbar_height;
+ return wp->w_border_adj[0] + wp->w_border_adj[2];
}
-static int win_extra_width(win_T *wp)
+static int win_border_width(win_T *wp)
{
return wp->w_border_adj[1] + wp->w_border_adj[3];
}
@@ -6481,7 +6480,7 @@ char_u *grab_file_name(long count, linenr_T *file_lnum)
*file_lnum = getdigits_long(&p, false, 0);
}
- return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
+ return find_file_name_in_path(ptr, len, options, count, (char_u *)curbuf->b_ffname);
}
return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
}
@@ -6502,7 +6501,7 @@ char_u *grab_file_name(long count, linenr_T *file_lnum)
char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum)
{
return file_name_in_line(get_cursor_line_ptr(),
- curwin->w_cursor.col, options, count, curbuf->b_ffname,
+ curwin->w_cursor.col, options, count, (char_u *)curbuf->b_ffname,
file_lnum);
}