diff options
author | James McCoy <jamessan@jamessan.com> | 2017-04-05 22:39:40 -0400 |
---|---|---|
committer | James McCoy <jamessan@jamessan.com> | 2017-04-05 22:39:40 -0400 |
commit | bb54d921aaf85f0393c1ba10585560056f7f4ec8 (patch) | |
tree | 316ff7424337b3572b58340383eeabb3a0d36e80 /src | |
parent | 4f69a8fb8854698adb2de8956ad0d86ff35a6f68 (diff) | |
parent | 210b013ce75b5ea8a897071e32decc1e1f88189e (diff) | |
download | rneovim-bb54d921aaf85f0393c1ba10585560056f7f4ec8.tar.gz rneovim-bb54d921aaf85f0393c1ba10585560056f7f4ec8.tar.bz2 rneovim-bb54d921aaf85f0393c1ba10585560056f7f4ec8.zip |
Merge remote-tracking branch 'origin/master' into vim-7.4.2170
Diffstat (limited to 'src')
148 files changed, 13273 insertions, 9131 deletions
diff --git a/src/.asan-blacklist b/src/.asan-blacklist index 7636f8fa82..928d81bd5a 100644 --- a/src/.asan-blacklist +++ b/src/.asan-blacklist @@ -1,3 +1,3 @@ # multiqueue.h pointer arithmetic is not accepted by asan fun:multiqueue_node_data -fun:dictwatcher_node_data +fun:tv_dict_watcher_node_data diff --git a/src/clint.py b/src/clint.py index ce31822ada..5174521fb8 100755 --- a/src/clint.py +++ b/src/clint.py @@ -571,7 +571,8 @@ class _CppLintState(object): for category, count in self.errors_by_category.items(): sys.stderr.write('Category \'%s\' errors found: %d\n' % (category, count)) - sys.stderr.write('Total errors found: %d\n' % self.error_count) + if self.error_count: + sys.stderr.write('Total errors found: %d\n' % self.error_count) def SuppressErrorsFrom(self, fname): """Open file and read a list of suppressed errors from it""" @@ -2268,11 +2269,14 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): # //!< Header comment # or they begin with multiple slashes followed by a space: # //////// Header comment + # or they are Vim {{{ fold markers match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or Search(r'^/$', line[commentend:]) or Search(r'^!< ', line[commentend:]) or Search(r'^/< ', line[commentend:]) or - Search(r'^/+ ', line[commentend:])) + Search(r'^/+ ', line[commentend:]) or + Search(r'^(?:\{{3}|\}{3})\d*(?: |$)', + line[commentend:])) if not match: error(filename, linenum, 'whitespace/comments', 4, 'Should have a space between // and comment') @@ -3575,7 +3579,7 @@ def main(): if __name__ == '__main__': main() -# vim: ts=4 sts=4 sw=4 +# vim: ts=4 sts=4 sw=4 foldmarker=▶,▲ # Ignore "too complex" warnings when using pymode. # pylama:ignore=C901 diff --git a/src/coverity-model.c b/src/coverity-model.c index a01ea6d316..3c38e4ae4d 100644 --- a/src/coverity-model.c +++ b/src/coverity-model.c @@ -64,7 +64,7 @@ void *je_realloc(void *ptr, size_t size) // of the memory allocated for item. typedef struct {} dictitem_T; typedef struct {} dict_T; -int dict_add(dict_T *d, dictitem_T *item) +int tv_dict_add(dict_T *const d, dictitem_T *const item) { __coverity_escape__(item); } diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 22cf1f3a3d..5a5ebc4415 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -10,6 +10,7 @@ if(USE_GCOV) endif() endif() +set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches) set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto) set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendispatch.lua) file(GLOB API_HEADERS api/*.h) @@ -22,7 +23,6 @@ set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.genera set(GENERATED_FUNCS_METADATA ${GENERATED_DIR}/api/private/funcs_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) -set(GENERATED_FUNCS_HASH_INPUT ${GENERATED_DIR}/funcs.generated.h.gperf) set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.generated.h) set(GENERATED_EVENTS_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h) set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h) @@ -31,23 +31,28 @@ set(EX_CMDS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genex_cmds.lua) set(FUNCS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/geneval.lua) set(EVENTS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gen_events.lua) set(OPTIONS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genoptions.lua) -set(EVENTS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/auevents.lua) -set(EX_CMDS_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/ex_cmds.lua) -set(EVAL_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/eval.lua) -set(OPTIONS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/options.lua) set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) +set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) +set(LINT_SUPPRESS_URL_BASE "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint") +set(LINT_SUPPRESS_URL "${LINT_SUPPRESS_URL_BASE}/errors.json") +set(LINT_PRG ${PROJECT_SOURCE_DIR}/src/clint.py) +set(DOWNLOAD_SCRIPT ${PROJECT_SOURCE_DIR}/cmake/Download.cmake) +set(LINT_SUPPRESSES_ROOT ${PROJECT_BINARY_DIR}/errors) +set(LINT_SUPPRESSES_URL "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint/errors.tar.gz") include_directories(${GENERATED_DIR}) include_directories(${CACHED_GENERATED_DIR}) include_directories(${GENERATED_INCLUDES_DIR}) +file(MAKE_DIRECTORY ${TOUCHES_DIR}) file(MAKE_DIRECTORY ${GENERATED_DIR}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) -file(GLOB NEOVIM_SOURCES *.c) +file(GLOB NVIM_SOURCES *.c) +file(GLOB NVIM_HEADERS *.h) foreach(subdir os @@ -65,18 +70,21 @@ foreach(subdir file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir}) file(GLOB sources ${subdir}/*.c) - list(APPEND NEOVIM_SOURCES ${sources}) + file(GLOB headers ${subdir}/*.h) + list(APPEND NVIM_SOURCES ${sources}) + list(APPEND NVIM_HEADERS ${headers}) endforeach() -file(GLOB_RECURSE NEOVIM_HEADERS *.h) file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) # Sort file lists to ensure generated files are created in the same order from # build to build. -list(SORT NEOVIM_SOURCES) -list(SORT NEOVIM_HEADERS) +list(SORT NVIM_SOURCES) +list(SORT NVIM_HEADERS) -foreach(sfile ${NEOVIM_SOURCES}) +list(APPEND LINT_NVIM_SOURCES ${NVIM_SOURCES} ${NVIM_HEADERS}) + +foreach(sfile ${NVIM_SOURCES}) get_filename_component(f ${sfile} NAME) if(${f} MATCHES "^(regexp_nfa.c)$") list(APPEND to_remove ${sfile}) @@ -86,7 +94,7 @@ foreach(sfile ${NEOVIM_SOURCES}) endif() endforeach() -list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove}) +list(REMOVE_ITEM NVIM_SOURCES ${to_remove}) # Legacy files that do not yet pass -Wconversion. set(CONV_SOURCES @@ -110,7 +118,7 @@ set(CONV_SOURCES window.c) foreach(sfile ${CONV_SOURCES}) - if(NOT EXISTS "${PROJECT_SOURCE_DIR}/src/nvim/${sfile}") + if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${sfile}") message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)") endif() endforeach() @@ -152,12 +160,24 @@ separate_arguments(C_FLAGS_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS}) separate_arguments(C_FLAGS_${build_type}_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS_${build_type}}) set(gen_cflags ${gen_cflags} ${C_FLAGS_${build_type}_ARRAY} ${C_FLAGS_ARRAY}) -foreach(sfile ${NEOVIM_SOURCES} - "${PROJECT_SOURCE_DIR}/src/nvim/regexp_nfa.c" +function(get_preproc_output varname iname) + if(MSVC) + set(${varname} /P /Fi${iname} PARENT_SCOPE) + else() + set(${varname} -E -o ${iname} PARENT_SCOPE) + endif() +endfunction() + +# NVIM_GENERATED_FOR_HEADERS: generated headers to be included in headers +# NVIM_GENERATED_FOR_SOURCES: generated headers to be included in sources +# NVIM_GENERATED_SOURCES: generated source files +# These lists must be mutually exclusive. +foreach(sfile ${NVIM_SOURCES} + "${CMAKE_CURRENT_LIST_DIR}/regexp_nfa.c" ${GENERATED_API_DISPATCH}) get_filename_component(full_d ${sfile} PATH) - file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") - if(${d} MATCHES "^([.][.]|auto/)") + file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") + if(${d} MATCHES "^[.][.]|auto/") file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") endif() get_filename_component(f ${sfile} NAME) @@ -166,26 +186,22 @@ foreach(sfile ${NEOVIM_SOURCES} set(f "${d}/${f}") set(r "${d}/${r}") endif() - set(gf1 "${GENERATED_DIR}/${r}.c.generated.h") - set(gf2 "${GENERATED_INCLUDES_DIR}/${r}.h.generated.h") - set(gf3 "${GENERATED_DIR}/${r}.i") + set(gf_c_h "${GENERATED_DIR}/${r}.c.generated.h") + set(gf_h_h "${GENERATED_INCLUDES_DIR}/${r}.h.generated.h") + set(gf_i "${GENERATED_DIR}/${r}.i") - if(MSVC) - set(PREPROC_OUTPUT /P /Fi${gf3}) - else() - set(PREPROC_OUTPUT -E -o ${gf3}) - endif() + get_preproc_output(PREPROC_OUTPUT ${gf_i}) add_custom_command( - OUTPUT "${gf1}" "${gf2}" + OUTPUT "${gf_c_h}" "${gf_h_h}" COMMAND ${CMAKE_C_COMPILER} ${sfile} ${PREPROC_OUTPUT} ${gen_cflags} ${C_FLAGS_ARRAY} - COMMAND "${LUA_PRG}" "${HEADER_GENERATOR}" "${sfile}" "${gf1}" "${gf2}" "${gf3}" + COMMAND "${LUA_PRG}" "${HEADER_GENERATOR}" "${sfile}" "${gf_c_h}" "${gf_h_h}" "${gf_i}" DEPENDS "${HEADER_GENERATOR}" "${sfile}" ) - list(APPEND NEOVIM_GENERATED_SOURCES "${gf1}") - list(APPEND NEOVIM_GENERATED_SOURCES "${gf2}") + list(APPEND NVIM_GENERATED_FOR_SOURCES "${gf_c_h}") + list(APPEND NVIM_GENERATED_FOR_HEADERS "${gf_h_h}") if(${d} MATCHES "^api$" AND NOT ${f} MATCHES "^api/helpers.c$") - list(APPEND API_HEADERS ${gf2}) + list(APPEND API_HEADERS ${gf_h_h}) endif() endforeach() @@ -210,21 +226,27 @@ add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} ${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua ) -list(APPEND NEOVIM_GENERATED_SOURCES - "${PROJECT_BINARY_DIR}/config/auto/pathdef.c" - "${GENERATED_API_DISPATCH}" +list(APPEND NVIM_GENERATED_FOR_HEADERS "${GENERATED_EX_CMDS_ENUM}" - "${GENERATED_EX_CMDS_DEFS}" "${GENERATED_EVENTS_ENUM}" +) + +list(APPEND NVIM_GENERATED_FOR_SOURCES + "${GENERATED_API_DISPATCH}" + "${GENERATED_EX_CMDS_DEFS}" "${GENERATED_EVENTS_NAMES_MAP}" "${GENERATED_OPTIONS}" "${GENERATED_UNICODE_TABLES}" ) +list(APPEND NVIM_GENERATED_SOURCES + "${PROJECT_BINARY_DIR}/config/auto/pathdef.c" +) + add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} COMMAND ${LUA_PRG} ${EX_CMDS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR} - DEPENDS ${EX_CMDS_GENERATOR} ${EX_CMDS_DEFS_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR} + DEPENDS ${EX_CMDS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/ex_cmds.lua ) if(NOT GPERF_PRG) @@ -232,26 +254,34 @@ if(NOT GPERF_PRG) endif() add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA} COMMAND ${LUA_PRG} ${FUNCS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_DIR} ${API_METADATA} ${FUNCS_DATA} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_DIR} ${API_METADATA} ${FUNCS_DATA} COMMAND ${GPERF_PRG} - ${GENERATED_FUNCS_HASH_INPUT} --output-file=${GENERATED_FUNCS} - DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} ${API_METADATA} + ${GENERATED_DIR}/funcs.generated.h.gperf --output-file=${GENERATED_FUNCS} + DEPENDS ${FUNCS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/eval.lua ${API_METADATA} ) -list(APPEND NEOVIM_GENERATED_SOURCES +list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_FUNCS}") add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} COMMAND ${LUA_PRG} ${EVENTS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} - DEPENDS ${EVENTS_GENERATOR} ${EVENTS_LIST_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} + DEPENDS ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua ) add_custom_command(OUTPUT ${GENERATED_OPTIONS} COMMAND ${LUA_PRG} ${OPTIONS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_OPTIONS} - DEPENDS ${OPTIONS_GENERATOR} ${OPTIONS_LIST_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_OPTIONS} + DEPENDS ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua ) +# NVIM_GENERATED_FOR_SOURCES and NVIM_GENERATED_FOR_HEADERS must be mutually exclusive. +foreach(hfile ${NVIM_GENERATED_FOR_HEADERS}) + list(FIND NVIM_GENERATED_FOR_SOURCES ${hfile} hfile_idx) + if(NOT ${hfile_idx} EQUAL -1) + message(FATAL_ERROR "File included in both NVIM_GENERATED_FOR_HEADERS and NVIM_GENERATED_FOR_SOURCES") + endif() +endforeach() + # Our dependencies come first. if (LibIntl_FOUND) @@ -286,8 +316,8 @@ if(JEMALLOC_FOUND) list(APPEND NVIM_EXEC_LINK_LIBRARIES ${JEMALLOC_LIBRARIES}) endif() -add_executable(nvim ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} - ${NEOVIM_HEADERS}) +add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}) target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) @@ -361,17 +391,146 @@ elseif(CLANG_TSAN) set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=thread ") endif() -add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} - ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) +add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}) target_link_libraries(libnvim ${NVIM_LINK_LIBRARIES}) set_target_properties(libnvim PROPERTIES POSITION_INDEPENDENT_CODE ON OUTPUT_NAME nvim) set_property(TARGET libnvim APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB ") -add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} - ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) +add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NVIM_HEADERS}) target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING) +function(get_test_target prefix sfile relative_path_var target_var) + get_filename_component(full_d "${sfile}" PATH) + file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") + if(d MATCHES "^[.][.]") + file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") + endif() + get_filename_component(r "${sfile}" NAME) + if(NOT d MATCHES "^[.]?$") + set(r "${d}/${r}") + endif() + string(REGEX REPLACE "[/.]" "-" suffix "${r}") + set(${relative_path_var} ${r} PARENT_SCOPE) + if(prefix STREQUAL "") + set(${target_var} "${suffix}" PARENT_SCOPE) + else() + set(${target_var} "${prefix}-${suffix}" PARENT_SCOPE) + endif() +endfunction() + +set(NO_SINGLE_CHECK_HEADERS + cursor_shape.h + diff.h + digraph.h + ex_cmds.h + ex_getln.h + file_search.h + fold.h + getchar.h + hardcopy.h + if_cscope.h + if_cscope_defs.h + mark.h + mbyte.h + memfile_defs.h + memline.h + memline_defs.h + menu.h + misc2.h + move.h + msgpack_rpc/server.h + ops.h + option.h + os/shell.h + os_unix.h + os/win_defs.h + popupmnu.h + quickfix.h + regexp.h + regexp_defs.h + screen.h + search.h + sha256.h + sign_defs.h + spell.h + spellfile.h + syntax.h + syntax_defs.h + tag.h + terminal.h + tui/tui.h + ugrid.h + ui.h + ui_bridge.h + undo.h + undo_defs.h + version.h + window.h +) +foreach(hfile ${NVIM_HEADERS}) + get_test_target(test-includes "${hfile}" relative_path texe) + + if(NOT ${hfile} MATCHES "[.]c[.]h$") + set(tsource "${GENERATED_DIR}/${relative_path}.test-include.c") + write_file("${tsource}" "#include \"${hfile}\"\nint main(int argc, char **argv) { return 0; }") + add_executable( + ${texe} + EXCLUDE_FROM_ALL + ${tsource} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_HEADERS}) + + list(FIND NO_SINGLE_CHECK_HEADERS "${relative_path}" hfile_exclude_idx) + if(${hfile_exclude_idx} EQUAL -1) + list(APPEND HEADER_CHECK_TARGETS ${texe}) + endif() + endif() +endforeach() +add_custom_target(check-single-includes DEPENDS ${HEADER_CHECK_TARGETS}) + +function(add_download output url allow_failure) + add_custom_command( + OUTPUT "${output}" + COMMAND + ${CMAKE_COMMAND} + -DURL=${url} -DFILE=${output} + -DALLOW_FAILURE=${allow_failure} + -P ${DOWNLOAD_SCRIPT} + DEPENDS ${DOWNLOAD_SCRIPT} + ) +endfunction() + +add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL} off) + +set(LINT_NVIM_REL_SOURCES) +foreach(sfile ${LINT_NVIM_SOURCES}) + get_test_target("" "${sfile}" r suffix) + set(suppress_file ${LINT_SUPPRESSES_ROOT}/${suffix}.json) + set(suppress_url "${LINT_SUPPRESS_URL_BASE}/${suffix}.json") + set(rsfile src/nvim/${r}) + add_download(${suppress_file} ${suppress_url} on) + set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}") + add_custom_command( + OUTPUT ${touch_file} + COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} ${rsfile} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -E touch ${touch_file} + DEPENDS ${LINT_PRG} ${sfile} ${suppress_file} + ) + list(APPEND LINT_TARGETS ${touch_file}) + list(APPEND LINT_NVIM_REL_SOURCES ${rsfile}) +endforeach() +add_custom_target(clint DEPENDS ${LINT_TARGETS}) + +add_custom_target( + clint-full + COMMAND + ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} ${LINT_NVIM_REL_SOURCES} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${LINT_PRG} ${LINT_NVIM_SOURCES} ${LINT_SUPPRESS_FILE} +) + add_subdirectory(po) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index b75a2c7211..26f9a6f592 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -296,7 +296,7 @@ void nvim_buf_set_lines(uint64_t channel_id, tabpage_T *save_curtab = NULL; size_t new_len = replacement.size; size_t old_len = (size_t)(end - start); - ssize_t extra = 0; // lines added to text, can be negative + ptrdiff_t extra = 0; // lines added to text, can be negative char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL; for (size_t i = 0; i < new_len; i++) { @@ -342,8 +342,8 @@ void nvim_buf_set_lines(uint64_t channel_id, } } - if ((ssize_t)to_delete > 0) { - extra -= (ssize_t)to_delete; + if (to_delete > 0) { + extra -= (ptrdiff_t)to_delete; } // For as long as possible, replace the existing old_len with the @@ -395,10 +395,10 @@ void nvim_buf_set_lines(uint64_t channel_id, mark_adjust((linenr_T)start, (linenr_T)(end - 1), MAXLNUM, extra); } - changed_lines((linenr_T)start, 0, (linenr_T)end, extra); + changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra); if (save_curbuf.br_buf == NULL) { - fix_cursor((linenr_T)start, (linenr_T)end, extra); + fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra); } end: diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7efa086af2..fe15b28041 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -14,6 +14,7 @@ #include "nvim/window.h" #include "nvim/memory.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/map_defs.h" #include "nvim/map.h" #include "nvim/option.h" @@ -87,14 +88,13 @@ bool try_end(Error *err) /// @param[out] err Details of an error that may have occurred Object dict_get_value(dict_T *dict, String key, Error *err) { - hashitem_T *hi = hash_find(&dict->dv_hashtab, (uint8_t *) key.data); + dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); - if (HASHITEM_EMPTY(hi)) { + if (di == NULL) { api_set_error(err, Validation, _("Key not found")); return (Object) OBJECT_INIT; } - dictitem_T *di = dict_lookup(hi); return vim_to_object(&di->di_tv); } @@ -129,7 +129,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, return rv; } - dictitem_T *di = dict_find(dict, (char_u *)key.data, (int)key.size); + dictitem_T *di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); if (di != NULL) { if (di->di_flags & DI_FLAGS_RO) { @@ -155,9 +155,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, rv = vim_to_object(&di->di_tv); } // Delete the entry - hashitem_T *hi = hash_find(&dict->dv_hashtab, di->di_key); - hash_remove(&dict->dv_hashtab, hi); - dictitem_free(di); + tv_dict_item_remove(dict, di); } } else { // Update the key @@ -170,20 +168,20 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, if (di == NULL) { // Need to create an entry - di = dictitem_alloc((uint8_t *) key.data); - dict_add(dict, di); + di = tv_dict_item_alloc_len(key.data, key.size); + tv_dict_add(dict, di); } else { // Return the old value if (retval) { rv = vim_to_object(&di->di_tv); } - clear_tv(&di->di_tv); + tv_clear(&di->di_tv); } // Update the value - copy_tv(&tv, &di->di_tv); + tv_copy(&tv, &di->di_tv); // Clear the temporary variable - clear_tv(&tv); + tv_clear(&tv); } return rv; @@ -291,7 +289,7 @@ void set_option_to(void *to, int type, String name, Object value, Error *err) } } - int opt_flags = (type ? OPT_LOCAL : OPT_GLOBAL); + int opt_flags = (type == SREQ_GLOBAL) ? OPT_GLOBAL : OPT_LOCAL; if (flags & SOPT_BOOL) { if (value.type != kObjectTypeBoolean) { @@ -627,7 +625,7 @@ String cstr_as_string(char *str) FUNC_ATTR_PURE if (str == NULL) { return (String) STRING_INIT; } - return (String) {.data = str, .size = strlen(str)}; + return (String) { .data = str, .size = strlen(str) }; } /// Converts from type Object to a VimL value. @@ -682,20 +680,20 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) break; case kObjectTypeArray: { - list_T *list = list_alloc(); + list_T *const list = tv_list_alloc(); for (uint32_t i = 0; i < obj.data.array.size; i++) { Object item = obj.data.array.items[i]; - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); if (!object_to_vim(item, &li->li_tv, err)) { // cleanup - listitem_free(li); - list_free(list); + tv_list_item_free(li); + tv_list_free(list); return false; } - list_append(list, li); + tv_list_append(list, li); } list->lv_refcount++; @@ -705,7 +703,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) } case kObjectTypeDictionary: { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); for (uint32_t i = 0; i < obj.data.dictionary.size; i++) { KeyValuePair item = obj.data.dictionary.items[i]; @@ -715,20 +713,20 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) api_set_error(err, Validation, _("Empty dictionary keys aren't allowed")); // cleanup - dict_free(dict); + tv_dict_free(dict); return false; } - dictitem_T *di = dictitem_alloc((uint8_t *)key.data); + dictitem_T *const di = tv_dict_item_alloc(key.data); if (!object_to_vim(item.value, &di->di_tv, err)) { // cleanup - dictitem_free(di); - dict_free(dict); + tv_dict_item_free(di); + tv_dict_free(dict); return false; } - dict_add(dict, di); + tv_dict_add(dict, di); } dict->dv_refcount++; @@ -959,11 +957,7 @@ static void set_option_value_err(char *key, { char *errmsg; - if ((errmsg = (char *)set_option_value((uint8_t *)key, - numval, - (uint8_t *)stringval, - opt_flags))) - { + if ((errmsg = set_option_value(key, numval, stringval, opt_flags))) { if (try_end(err)) { return; } diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 625bcc6b4b..de60339e5f 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -12,6 +12,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/popupmnu.h" +#include "nvim/cursor_shape.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" @@ -69,6 +70,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, ui->clear = remote_ui_clear; ui->eol_clear = remote_ui_eol_clear; ui->cursor_goto = remote_ui_cursor_goto; + ui->cursor_style_set = remote_ui_cursor_style_set; ui->update_menu = remote_ui_update_menu; ui->busy_start = remote_ui_busy_start; ui->busy_stop = remote_ui_busy_stop; @@ -298,6 +300,14 @@ static void remote_ui_scroll(UI *ui, int count) push_call(ui, "scroll", args); } +static void remote_ui_cursor_style_set(UI *ui, bool enabled, Dictionary data) +{ + Array args = ARRAY_DICT_INIT; + ADD(args, BOOLEAN_OBJ(enabled)); + ADD(args, copy_object(DICTIONARY_OBJ(data))); + push_call(ui, "cursor_style_set", args); +} + static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) { Array args = ARRAY_DICT_INIT; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 413456c615..6926436d2f 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -22,6 +22,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/option.h" #include "nvim/syntax.h" #include "nvim/getchar.h" @@ -181,19 +182,20 @@ Object nvim_eval(String expr, Error *err) Object rv = OBJECT_INIT; // Evaluate the expression try_start(); - typval_T *expr_result = eval_expr((char_u *)expr.data, NULL); - if (!expr_result) { - api_set_error(err, Exception, _("Failed to evaluate expression")); + typval_T rettv; + if (eval0((char_u *)expr.data, &rettv, NULL, true) == FAIL) { + api_set_error(err, Exception, "Failed to evaluate expression"); } if (!try_end(err)) { // No errors, convert the result - rv = vim_to_object(expr_result); + rv = vim_to_object(&rettv); } - // Free the vim object - free_tv(expr_result); + // Free the Vim object + tv_clear(&rettv); + return rv; } @@ -237,11 +239,11 @@ Object nvim_call_function(String fname, Array args, Error *err) if (!try_end(err)) { rv = vim_to_object(&rettv); } - clear_tv(&rettv); + tv_clear(&rettv); free_vim_args: while (i > 0) { - clear_tv(&vim_args[--i]); + tv_clear(&vim_args[--i]); } return rv; @@ -438,7 +440,7 @@ Object nvim_get_vvar(String name, Error *err) /// /// @param name Option name /// @param[out] err Error details, if any -/// @return Option value +/// @return Option value (global) Object nvim_get_option(String name, Error *err) FUNC_API_SINCE(1) { diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 44ff540b40..adde91f9ec 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -8,9 +8,11 @@ // Definitions of various common control characters. -#define CharOrd(x) ((x) < 'a' ? (x) - 'A' : (x) - 'a') -#define CharOrdLow(x) ((x) - 'a') -#define CharOrdUp(x) ((x) - 'A') +#define CharOrd(x) ((uint8_t)(x) < 'a' \ + ? (uint8_t)(x) - 'A'\ + : (uint8_t)(x) - 'a') +#define CharOrdLow(x) ((uint8_t)(x) - 'a') +#define CharOrdUp(x) ((uint8_t)(x) - 'A') #define ROT13(c, a) (((((c) - (a)) + 13) % 26) + (a)) #define NUL '\000' @@ -18,15 +20,14 @@ #define BS '\010' #define TAB '\011' #define NL '\012' -#define NL_STR (char_u *)"\012" +#define NL_STR "\012" #define FF '\014' #define CAR '\015' /* CR is used by Mac OS X */ #define ESC '\033' -#define ESC_STR (char_u *)"\033" -#define ESC_STR_nc "\033" +#define ESC_STR "\033" #define DEL 0x7f -#define DEL_STR (char_u *)"\177" -#define CSI 0x9b /* Control Sequence Introducer */ +#define DEL_STR "\177" +#define CSI 0x9b // Control Sequence Introducer #define CSI_STR "\233" #define DCS 0x90 /* Device Control String */ #define STERM 0x9c /* String Terminator */ diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 9e781f5dff..292eb03a16 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -337,6 +337,10 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE); bool wipe_buf = (action == DOBUF_WIPE); + bool is_curwin = (curwin != NULL && curwin->w_buffer == buf); + win_T *the_curwin = curwin; + tabpage_T *the_curtab = curtab; + // Force unloading or deleting when 'bufhidden' says so, but not for terminal // buffers. // The caller must take care of NOT deleting/freeing when 'bufhidden' is @@ -360,6 +364,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) wipe_buf = true; } + // 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")); + return; + } + if (win_valid_any_tab(win)) { // Set b_last_cursor when closing the last window for the buffer. // Remember the last cursor position and window options of the buffer. @@ -378,14 +389,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) /* When the buffer is no longer in a window, trigger BufWinLeave */ if (buf->b_nwindows == 1) { - buf->b_closing = true; + buf->b_locked++; if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false, buf) && !bufref_valid(&bufref)) { // Autocommands deleted the buffer. EMSG(_(e_auabort)); return; } - buf->b_closing = false; + buf->b_locked--; if (abort_if_last && one_window()) { /* Autocommands made this the only window. */ EMSG(_(e_auabort)); @@ -395,14 +406,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) /* When the buffer becomes hidden, but is not unloaded, trigger * BufHidden */ if (!unload_buf) { - buf->b_closing = true; + buf->b_locked++; if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false, buf) && !bufref_valid(&bufref)) { // Autocommands deleted the buffer. EMSG(_(e_auabort)); return; } - buf->b_closing = false; + buf->b_locked--; if (abort_if_last && one_window()) { /* Autocommands made this the only window. */ EMSG(_(e_auabort)); @@ -412,6 +423,16 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) if (aborting()) /* autocmds may abort script processing */ return; } + + // If the buffer was in curwin and the window has changed, go back to that + // window, if it still exists. This avoids that ":edit x" triggering a + // "tabnext" BufUnload autocmd leaves a window behind without a buffer. + if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) { + block_autocmds(); + goto_tabpage_win(the_curtab, the_curwin); + unblock_autocmds(); + } + int nwindows = buf->b_nwindows; /* decrease the link count from windows (unless not in any window) */ @@ -559,7 +580,7 @@ void buf_freeall(buf_T *buf, int flags) tabpage_T *the_curtab = curtab; // Make sure the buffer isn't closed by autocommands. - buf->b_closing = true; + buf->b_locked++; bufref_T bufref; set_bufref(&bufref, buf); @@ -584,7 +605,7 @@ void buf_freeall(buf_T *buf, int flags) // Autocommands may delete the buffer. return; } - buf->b_closing = false; + buf->b_locked--; // If the buffer was in curwin and the window has changed, go back to that // window, if it still exists. This avoids that ":edit x" triggering a @@ -606,10 +627,11 @@ void buf_freeall(buf_T *buf, int flags) */ if (buf == curbuf && !is_curbuf) return; - diff_buf_delete(buf); /* Can't use 'diff' for unloaded buffer. */ - /* Remove any ownsyntax, unless exiting. */ - if (firstwin != NULL && curwin->w_buffer == buf) + diff_buf_delete(buf); // Can't use 'diff' for unloaded buffer. + // Remove any ownsyntax, unless exiting. + if (curwin != NULL && curwin->w_buffer == buf) { reset_synblock(curwin); + } /* No folds in an empty buffer. */ FOR_ALL_TAB_WINDOWS(tp, win) { @@ -639,7 +661,7 @@ static void free_buffer(buf_T *buf) free_buffer_stuff(buf, true); unref_var_dict(buf->b_vars); aubuflocal_remove(buf); - dict_unref(buf->additional_data); + tv_dict_unref(buf->additional_data); clear_fmark(&buf->b_last_cursor); clear_fmark(&buf->b_last_insert); clear_fmark(&buf->b_last_change); @@ -1111,7 +1133,7 @@ do_buffer ( * a window with this buffer. */ while (buf == curbuf - && !(curwin->w_closing || curwin->w_buffer->b_closing) + && !(curwin->w_closing || curwin->w_buffer->b_locked > 0) && (firstwin != lastwin || first_tabpage->tp_next != NULL)) { if (win_close(curwin, FALSE) == FAIL) break; @@ -1450,7 +1472,7 @@ static inline void buf_init_changedtick(buf_T *const buf) { STATIC_ASSERT(sizeof("changedtick") <= sizeof(buf->changedtick_di.di_key), "buf->changedtick_di cannot hold large enough keys"); - buf->changedtick_di = (dictitem16_T) { + buf->changedtick_di = (ChangedtickDictItem) { .di_flags = DI_FLAGS_RO|DI_FLAGS_FIX, // Must not include DI_FLAGS_ALLOC. .di_tv = (typval_T) { .v_type = VAR_NUMBER, @@ -1459,7 +1481,7 @@ static inline void buf_init_changedtick(buf_T *const buf) }, .di_key = "changedtick", }; - dict_add(buf->b_vars, (dictitem_T *)&buf->changedtick_di); + tv_dict_add(buf->b_vars, (dictitem_T *)&buf->changedtick_di); } /// Add a file name to the buffer list. @@ -1551,7 +1573,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) if (buf != curbuf || curbuf == NULL) { buf = xcalloc(1, sizeof(buf_T)); // init b: variables - buf->b_vars = dict_alloc(); + buf->b_vars = tv_dict_alloc(); init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); buf_init_changedtick(buf); } @@ -2702,7 +2724,7 @@ fileinfo ( else name = curbuf->b_ffname; home_replace(shorthelp ? curbuf : NULL, name, p, - (int)(IOSIZE - (p - buffer)), TRUE); + (size_t)(IOSIZE - (p - buffer)), true); } vim_snprintf_add((char *)buffer, IOSIZE, "\"%s%s%s%s%s%s", @@ -2867,7 +2889,7 @@ void maketitle(void) buf[off++] = ' '; buf[off++] = '('; home_replace(curbuf, curbuf->b_ffname, - buf + off, SPACE_FOR_DIR - off, TRUE); + buf + off, (size_t)(SPACE_FOR_DIR - off), true); #ifdef BACKSLASH_IN_FILENAME /* avoid "c:/name" to be reduced to "c" */ if (isalpha(buf[off]) && buf[off + 1] == ':') @@ -3482,7 +3504,7 @@ int build_stl_str_hl( curbuf = o_curbuf; // Remove the variable we just stored - do_unlet((char_u *)"g:actual_curbuf", true); + do_unlet(S_LEN("g:actual_curbuf"), true); // } @@ -4185,11 +4207,11 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname) #ifdef WIN32 if (!buf->b_p_bin) { // If the file name is a shortcut file, use the file it links to. - char_u *rfname = (char_u *)os_resolve_shortcut(*ffname); + char *rfname = os_resolve_shortcut((const char *)(*ffname)); if (rfname != NULL) { xfree(*ffname); - *ffname = rfname; - *sfname = rfname; + *ffname = (char_u *)rfname; + *sfname = (char_u *)rfname; } } #endif @@ -4497,9 +4519,9 @@ void ex_buffer_all(exarg_T *eap) ? wp->w_height + wp->w_status_height < Rows - p_ch - tabline_height() : wp->w_width != Columns) - || (had_tab > 0 && wp != firstwin) - ) && firstwin != lastwin - && !(wp->w_closing || wp->w_buffer->b_closing) + || (had_tab > 0 && wp != firstwin)) + && firstwin != lastwin + && !(wp->w_closing || wp->w_buffer->b_locked > 0) ) { win_close(wp, FALSE); wpnext = firstwin; /* just in case an autocommand does @@ -5421,8 +5443,8 @@ void buf_open_scratch(handle_T bufnr, char *bufname) { (void)do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL); (void)setfname(curbuf, (char_u *)bufname, NULL, true); - set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL); - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); + set_option_value("bh", 0L, "hide", OPT_LOCAL); + set_option_value("bt", 0L, "nofile", OPT_LOCAL); + set_option_value("swf", 0L, NULL, OPT_LOCAL); RESET_BINDING(curwin); } diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index ed3e6ab6cc..016c5ce3b7 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -1,12 +1,14 @@ #ifndef NVIM_BUFFER_H #define NVIM_BUFFER_H +#include "nvim/vim.h" #include "nvim/window.h" #include "nvim/pos.h" // for linenr_T #include "nvim/ex_cmds_defs.h" // for exarg_T #include "nvim/screen.h" // for StlClickRecord #include "nvim/func_attr.h" #include "nvim/eval.h" +#include "nvim/macros.h" // Values for buflist_getfile() enum getf_values { @@ -91,8 +93,8 @@ static inline void buf_set_changedtick(buf_T *const buf, const int changedtick) static inline void buf_set_changedtick(buf_T *const buf, const int changedtick) { #ifndef NDEBUG - dictitem_T *const changedtick_di = dict_find( - buf->b_vars, (char_u *)"changedtick", sizeof("changedtick") - 1); + dictitem_T *const changedtick_di = tv_dict_find( + buf->b_vars, S_LEN("changedtick")); assert(changedtick_di != NULL); assert(changedtick_di->di_tv.v_type == VAR_NUMBER); assert(changedtick_di->di_tv.v_lock == VAR_FIXED); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index fdd7d945c9..20a2b931bd 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -21,8 +21,6 @@ typedef struct { #include "nvim/pos.h" // for the number window-local and buffer-local options #include "nvim/option_defs.h" -// for optional iconv support -#include "nvim/iconv.h" // for jump list and tag stack sizes in a buffer and mark types #include "nvim/mark_defs.h" // for u_header_T; needs buf_T. @@ -30,7 +28,9 @@ typedef struct { // for hashtab_T #include "nvim/hashtab.h" // for dict_T -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" +// for proftime_T +#include "nvim/profile.h" // for String #include "nvim/api/private/defs.h" // for Map(K, V) @@ -319,25 +319,6 @@ typedef struct { } tasave_T; /* - * Used for conversion of terminal I/O and script files. - */ -typedef struct { - int vc_type; /* zero or one of the CONV_ values */ - int vc_factor; /* max. expansion factor */ -# ifdef USE_ICONV - iconv_t vc_fd; /* for CONV_ICONV */ -# endif - bool vc_fail; /* fail for invalid char, don't use '?' */ -} vimconv_T; - -#define CONV_NONE 0 -#define CONV_TO_UTF8 1 -#define CONV_9_TO_UTF8 2 -#define CONV_TO_LATIN1 3 -#define CONV_TO_LATIN9 4 -#define CONV_ICONV 5 - -/* * Structure used for mappings and abbreviations. */ typedef struct mapblock mapblock_T; @@ -447,6 +428,10 @@ typedef struct { char_u *b_syn_isk; // iskeyword option } synblock_T; +/// Type used for changedtick_di member in buf_T +/// +/// Primary exists so that literals of relevant type can be made. +typedef TV_DICTITEM_STRUCT(sizeof("changedtick")) ChangedtickDictItem; #define BUF_HAS_QF_ENTRY 1 #define BUF_HAS_LL_ENTRY 2 @@ -470,9 +455,9 @@ struct file_buffer { int b_nwindows; /* nr of windows open on this buffer */ - int b_flags; /* various BF_ flags */ - bool b_closing; /* buffer is being closed, don't let - autocommands close it too. */ + int b_flags; // various BF_ flags + int b_locked; // Buffer is being closed or referenced, don't + // let autocommands wipe it out. /* * b_ffname has the full path of the file (NULL for no name). @@ -491,7 +476,7 @@ struct file_buffer { // file has been changed and not written out. /// Change identifier incremented for each change, including undo #define b_changedtick changedtick_di.di_tv.vval.v_number - dictitem16_T changedtick_di; // b:changedtick dictionary item. + ChangedtickDictItem changedtick_di; // b:changedtick dictionary item. bool b_saving; /* Set to true if we are in the middle of saving the buffer. */ @@ -735,8 +720,8 @@ struct file_buffer { int b_bad_char; /* "++bad=" argument when edit started or 0 */ int b_start_bomb; /* 'bomb' when it was read */ - dictitem_T b_bufvar; /* variable for "b:" Dictionary */ - dict_T *b_vars; /* internal variables, local to buffer */ + ScopeDictDictItem b_bufvar; ///< Variable for "b:" Dictionary. + dict_T *b_vars; ///< b: scope dictionary. /* When a buffer is created, it starts without a swap file. b_may_swap is * then set to indicate that a swap file may be opened later. It is reset @@ -824,9 +809,9 @@ struct tabpage_S { buf_T *(tp_diffbuf[DB_COUNT]); int tp_diff_invalid; ///< list of diffs is outdated frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots - dictitem_T tp_winvar; ///< variable for "t:" Dictionary - dict_T *tp_vars; ///< internal variables, local to tab page - char_u *tp_localdir; ///< Absolute path of local CWD or NULL + 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. }; /* @@ -1118,8 +1103,8 @@ struct window_S { long w_scbind_pos; - dictitem_T w_winvar; /* variable for "w:" Dictionary */ - dict_T *w_vars; /* internal variables, local to window */ + ScopeDictDictItem w_winvar; ///< Variable for "w:" dictionary. + dict_T *w_vars; ///< Dictionary with w: variables. int w_farsi; /* for the window dependent Farsi functions */ diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 9e240fd38b..99d3e2dd88 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -41,8 +41,10 @@ static bool chartab_initialized = false; (buf)->b_chartab[(unsigned)(c) >> 6] |= (1ull << ((c) & 0x3f)) #define RESET_CHARTAB(buf, c) \ (buf)->b_chartab[(unsigned)(c) >> 6] &= ~(1ull << ((c) & 0x3f)) +#define GET_CHARTAB_TAB(chartab, c) \ + ((chartab)[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) #define GET_CHARTAB(buf, c) \ - ((buf)->b_chartab[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) + GET_CHARTAB_TAB((buf)->b_chartab, c) // Table used below, see init_chartab() for an explanation static char_u g_chartab[256]; @@ -88,7 +90,6 @@ int buf_init_chartab(buf_T *buf, int global) { int c; int c2; - char_u *p; int i; bool tilde; bool do_isalpha; @@ -142,7 +143,8 @@ int buf_init_chartab(buf_T *buf, int global) // Walk through the 'isident', 'iskeyword', 'isfname' and 'isprint' // options Each option is a list of characters, character numbers or // ranges, separated by commas, e.g.: "200-210,x,#-178,-" - for (i = global ? 0 : 3; i <= 3; ++i) { + for (i = global ? 0 : 3; i <= 3; i++) { + const char_u *p; if (i == 0) { // first round: 'isident' p = p_isi; @@ -167,7 +169,7 @@ int buf_init_chartab(buf_T *buf, int global) } if (ascii_isdigit(*p)) { - c = getdigits_int(&p); + c = getdigits_int((char_u **)&p); } else { c = mb_ptr2char_adv(&p); } @@ -177,7 +179,7 @@ int buf_init_chartab(buf_T *buf, int global) ++p; if (ascii_isdigit(*p)) { - c2 = getdigits_int(&p); + c2 = getdigits_int((char_u **)&p); } else { c2 = mb_ptr2char_adv(&p); } @@ -634,7 +636,7 @@ int char2cells(int c) /// @param p /// /// @return number of display cells. -int ptr2cells(char_u *p) +int ptr2cells(const char_u *p) { // For UTF-8 we need to look at more bytes if the first byte is >= 0x80. if (*p >= 0x80) { @@ -776,6 +778,20 @@ bool vim_iswordc(int c) return vim_iswordc_buf(c, curbuf); } +/// Check that "c" is a keyword character +/// Letters and characters from 'iskeyword' option for given buffer. +/// For multi-byte characters mb_get_class() is used (builtin rules). +/// +/// @param[in] c Character to check. +/// @param[in] chartab Buffer chartab. +bool vim_iswordc_tab(const int c, const uint64_t *const chartab) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + return (c >= 0x100 + ? (utf_class(c) >= 2) + : (c > 0 && GET_CHARTAB_TAB(chartab, c) != 0)); +} + /// Check that "c" is a keyword character: /// Letters and characters from 'iskeyword' option for given buffer. /// For multi-byte characters mb_get_class() is used (builtin rules). @@ -785,10 +801,7 @@ bool vim_iswordc(int c) bool vim_iswordc_buf(int c, buf_T *buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2) { - if (c >= 0x100) { - return utf_class(c) >= 2; - } - return c > 0 && c < 0x100 && GET_CHARTAB(buf, c) != 0; + return vim_iswordc_tab(c, buf->b_chartab); } /// Just like vim_iswordc() but uses a pointer to the (multi-byte) character. @@ -1160,7 +1173,13 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, // continue until the NUL posptr = NULL; } else { + // Special check for an empty line, which can happen on exit, when + // ml_get_buf() always returns an empty string. + if (*ptr == NUL) { + pos->col = 0; + } posptr = ptr + pos->col; + posptr -= utf_head_off(line, posptr); } // This function is used very often, do some speed optimizations. @@ -1378,7 +1397,8 @@ void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, /// /// @return Pointer to character after the skipped whitespace. char_u *skipwhite(const char_u *q) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET { const char_u *p = q; while (ascii_iswhite(*p)) { @@ -1387,19 +1407,21 @@ char_u *skipwhite(const char_u *q) return (char_u *)p; } -/// skip over digits +/// Skip over digits /// -/// @param q +/// @param[in] q String to skip digits in. /// /// @return Pointer to the character after the skipped digits. -char_u* skipdigits(char_u *q) +char_u *skipdigits(const char_u *q) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET { - char_u *p = q; + const char_u *p = q; while (ascii_isdigit(*p)) { // skip to next non-digit p++; } - return p; + return (char_u *)p; } /// skip over binary digits @@ -1545,17 +1567,17 @@ int vim_tolower(int c) return TOLOWER_LOC(c); } -/// skiptowhite: skip over text until ' ' or '\t' or NUL. +/// Skip over text until ' ' or '\t' or NUL /// -/// @param p +/// @param[in] p Text to skip over. /// /// @return Pointer to the next whitespace or NUL character. -char_u* skiptowhite(char_u *p) +char_u *skiptowhite(const char_u *p) { while (*p != ' ' && *p != '\t' && *p != NUL) { p++; } - return p; + return (char_u *)p; } /// skiptowhite_esc: Like skiptowhite(), but also skip escaped chars diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 78d6f2a76c..c69582c4c6 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -1,6 +1,20 @@ #ifndef NVIM_CHARSET_H #define NVIM_CHARSET_H +#include "nvim/types.h" +#include "nvim/pos.h" +#include "nvim/buffer_defs.h" + +/// Return the folded-case equivalent of the given character +/// +/// @param[in] c Character to transform. +/// +/// @return Folded variant. +#define CH_FOLD(c) \ + utf_fold((sizeof(c) == sizeof(char)) \ + ?((int)(uint8_t)(c)) \ + :((int)(c))) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "charset.h.generated.h" #endif diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 544bcf6ede..45abd314fc 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -12,6 +12,7 @@ #include "nvim/state.h" #include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/mark.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "cursor.c.generated.h" @@ -227,9 +228,10 @@ static int coladvance2( } } - /* prevent from moving onto a trail byte */ - if (has_mbyte) - mb_adjustpos(curbuf, pos); + // Prevent from moving onto a trail byte. + if (has_mbyte) { + mark_mb_adjustpos(curbuf, pos); + } if (col < wcol) return FAIL; @@ -338,9 +340,8 @@ void check_cursor_col(void) check_cursor_col_win(curwin); } -/* - * Make sure win->w_cursor.col is valid. - */ +/// Make sure win->w_cursor.col is valid. Special handling of insert-mode. +/// @see mb_check_adjust_col void check_cursor_col_win(win_T *win) { colnr_T len; @@ -362,9 +363,10 @@ void check_cursor_col_win(win_T *win) win->w_cursor.col = len; } else { win->w_cursor.col = len - 1; - /* Move the cursor to the head byte. */ - if (has_mbyte) - mb_adjustpos(win->w_buffer, &win->w_cursor); + // Move the cursor to the head byte. + if (has_mbyte) { + mark_mb_adjustpos(win->w_buffer, &win->w_cursor); + } } } else if (win->w_cursor.col < 0) { win->w_cursor.col = 0; diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index b50462664c..34ee53bf75 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -7,40 +7,74 @@ #include "nvim/charset.h" #include "nvim/strings.h" #include "nvim/syntax.h" +#include "nvim/api/private/helpers.h" +#include "nvim/ui.h" -/* - * Handling of cursor and mouse pointer shapes in various modes. - */ - +/// Handling of cursor and mouse pointer shapes in various modes. static cursorentry_T shape_table[SHAPE_IDX_COUNT] = { - /* The values will be filled in from the 'guicursor' and 'mouseshape' - * defaults when Vim starts. - * Adjust the SHAPE_IDX_ defines when making changes! */ - {0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE}, - {0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR}, + // Values are set by 'guicursor' and 'mouseshape'. + // Adjust the SHAPE_IDX_ defines when changing this! + { "normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE }, + { "visual", 0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE }, + { "insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE }, + { "replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE }, + { "operator", 0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE }, + { "visual_select", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE }, + { "statusline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE }, + { "statusline_drag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE }, + { "vsep_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE }, + { "vsep_drag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE }, + { "more", 0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE }, + { "more_lastline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE }, + { "showmatch", 0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR }, }; -/* - * Parse the 'guicursor' option ("what" is SHAPE_CURSOR) or 'mouseshape' - * ("what" is SHAPE_MOUSE). - * Returns error message for an illegal option, NULL otherwise. - */ +/// Converts cursor_shapes into a Dictionary of dictionaries +/// @return dictionary of the form {"normal" : { "cursor_shape": ... }, ...} +Dictionary cursor_shape_dict(void) +{ + Dictionary all = ARRAY_DICT_INIT; + + for (int i = 0; i < SHAPE_IDX_COUNT; i++) { + Dictionary dic = ARRAY_DICT_INIT; + cursorentry_T *cur = &shape_table[i]; + if (cur->used_for & SHAPE_MOUSE) { + PUT(dic, "mouse_shape", INTEGER_OBJ(cur->mshape)); + } + if (cur->used_for & SHAPE_CURSOR) { + String shape_str; + switch (cur->shape) { + case SHAPE_BLOCK: shape_str = cstr_to_string("block"); break; + case SHAPE_VER: shape_str = cstr_to_string("vertical"); break; + case SHAPE_HOR: shape_str = cstr_to_string("horizontal"); break; + default: shape_str = cstr_to_string("unknown"); + } + PUT(dic, "cursor_shape", STRING_OBJ(shape_str)); + PUT(dic, "cell_percentage", INTEGER_OBJ(cur->percentage)); + PUT(dic, "blinkwait", INTEGER_OBJ(cur->blinkwait)); + PUT(dic, "blinkon", INTEGER_OBJ(cur->blinkon)); + PUT(dic, "blinkoff", INTEGER_OBJ(cur->blinkoff)); + PUT(dic, "hl_id", INTEGER_OBJ(cur->id)); + PUT(dic, "id_lm", INTEGER_OBJ(cur->id_lm)); + } + PUT(dic, "short_name", STRING_OBJ(cstr_to_string(cur->name))); + + PUT(all, cur->full_name, DICTIONARY_OBJ(dic)); + } + + return all; +} + +/// Parse the 'guicursor' option +/// +/// @param what SHAPE_CURSOR or SHAPE_MOUSE ('mouseshape') +/// +/// @returns error message for an illegal option, NULL otherwise. char_u *parse_shape_opt(int what) { char_u *modep; @@ -55,14 +89,13 @@ char_u *parse_shape_opt(int what) int found_ve = false; /* found "ve" flag */ int round; - /* - * First round: check for errors; second round: do it for real. - */ - for (round = 1; round <= 2; ++round) { - /* - * Repeat for all comma separated parts. - */ + // First round: check for errors; second round: do it for real. + for (round = 1; round <= 2; round++) { + // Repeat for all comma separated parts. modep = p_guicursor; + if (*p_guicursor == NUL) { + modep = (char_u *)"a:block-blinkon0"; + } while (*modep != NUL) { colonp = vim_strchr(modep, ':'); if (colonp == NULL) @@ -71,19 +104,18 @@ char_u *parse_shape_opt(int what) return (char_u *)N_("E546: Illegal mode"); commap = vim_strchr(modep, ','); - /* - * Repeat for all mode's before the colon. - * For the 'a' mode, we loop to handle all the modes. - */ + // Repeat for all modes before the colon. + // For the 'a' mode, we loop to handle all the modes. all_idx = -1; assert(modep < colonp); while (modep < colonp || all_idx >= 0) { if (all_idx < 0) { - /* Find the mode. */ - if (modep[1] == '-' || modep[1] == ':') + // Find the mode + if (modep[1] == '-' || modep[1] == ':') { len = 1; - else + } else { len = 2; + } if (len == 1 && TOLOWER_ASC(modep[0]) == 'a') { all_idx = SHAPE_IDX_COUNT - 1; @@ -100,11 +132,11 @@ char_u *parse_shape_opt(int what) modep += len + 1; } - if (all_idx >= 0) + if (all_idx >= 0) { idx = all_idx--; - else if (round == 2) { + } else if (round == 2) { { - /* Set the defaults, for the missing parts */ + // Set the defaults, for the missing parts shape_table[idx].shape = SHAPE_BLOCK; shape_table[idx].blinkwait = 700L; shape_table[idx].blinkon = 400L; @@ -208,6 +240,23 @@ char_u *parse_shape_opt(int what) shape_table[SHAPE_IDX_VE].id_lm = shape_table[SHAPE_IDX_V].id_lm; } } - + ui_cursor_style_set(); return NULL; } + + +/// Map cursor mode from string to integer +/// +/// @param mode Fullname of the mode whose id we are looking for +/// @return -1 in case of failure, else the matching SHAPE_ID* integer +int cursor_mode_str2int(const char *mode) +{ + for (int current_mode = 0; current_mode < SHAPE_IDX_COUNT; current_mode++) { + if (strcmp(shape_table[current_mode].full_name, mode) == 0) { + return current_mode; + } + } + ELOG("Unknown mode %s", mode); + return -1; +} + diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h index 9ce1b6e0a0..0006ede31d 100644 --- a/src/nvim/cursor_shape.h +++ b/src/nvim/cursor_shape.h @@ -1,32 +1,34 @@ #ifndef NVIM_CURSOR_SHAPE_H #define NVIM_CURSOR_SHAPE_H -/* - * struct to store values from 'guicursor' and 'mouseshape' - */ -/* Indexes in shape_table[] */ -#define SHAPE_IDX_N 0 /* Normal mode */ -#define SHAPE_IDX_V 1 /* Visual mode */ -#define SHAPE_IDX_I 2 /* Insert mode */ -#define SHAPE_IDX_R 3 /* Replace mode */ -#define SHAPE_IDX_C 4 /* Command line Normal mode */ -#define SHAPE_IDX_CI 5 /* Command line Insert mode */ -#define SHAPE_IDX_CR 6 /* Command line Replace mode */ -#define SHAPE_IDX_O 7 /* Operator-pending mode */ -#define SHAPE_IDX_VE 8 /* Visual mode with 'selection' exclusive */ -#define SHAPE_IDX_CLINE 9 /* On command line */ -#define SHAPE_IDX_STATUS 10 /* A status line */ -#define SHAPE_IDX_SDRAG 11 /* dragging a status line */ -#define SHAPE_IDX_VSEP 12 /* A vertical separator line */ -#define SHAPE_IDX_VDRAG 13 /* dragging a vertical separator line */ -#define SHAPE_IDX_MORE 14 /* Hit-return or More */ -#define SHAPE_IDX_MOREL 15 /* Hit-return or More in last line */ -#define SHAPE_IDX_SM 16 /* showing matching paren */ -#define SHAPE_IDX_COUNT 17 +/// struct to store values from 'guicursor' and 'mouseshape' +/// Indexes in shape_table[] +typedef enum { +SHAPE_IDX_N = 0, ///< Normal mode +SHAPE_IDX_V = 1, ///< Visual mode +SHAPE_IDX_I = 2, ///< Insert mode +SHAPE_IDX_R = 3, ///< Replace mode +SHAPE_IDX_C = 4, ///< Command line Normal mode +SHAPE_IDX_CI = 5, ///< Command line Insert mode +SHAPE_IDX_CR = 6, ///< Command line Replace mode +SHAPE_IDX_O = 7, ///< Operator-pending mode +SHAPE_IDX_VE = 8, ///< Visual mode with 'selection' exclusive +SHAPE_IDX_CLINE = 9, ///< On command line +SHAPE_IDX_STATUS = 10, ///< On status line +SHAPE_IDX_SDRAG = 11, ///< dragging a status line +SHAPE_IDX_VSEP = 12, ///< On vertical separator line +SHAPE_IDX_VDRAG = 13, ///< dragging a vertical separator line +SHAPE_IDX_MORE = 14, ///< Hit-return or More +SHAPE_IDX_MOREL = 15, ///< Hit-return or More in last line +SHAPE_IDX_SM = 16, ///< showing matching paren +SHAPE_IDX_COUNT = 17 +} MouseMode; -#define SHAPE_BLOCK 0 /* block cursor */ -#define SHAPE_HOR 1 /* horizontal bar cursor */ -#define SHAPE_VER 2 /* vertical bar cursor */ +typedef enum { +SHAPE_BLOCK = 0, ///< block cursor +SHAPE_HOR = 1, ///< horizontal bar cursor +SHAPE_VER = 2 ///< vertical bar cursor +} CursorShape; #define MSHAPE_NUMBERED 1000 /* offset for shapes identified by number */ #define MSHAPE_HIDE 1 /* hide mouse pointer */ @@ -35,16 +37,17 @@ #define SHAPE_CURSOR 2 /* used for text cursor shape */ typedef struct cursor_entry { - int shape; /* one of the SHAPE_ defines */ - int mshape; /* one of the MSHAPE defines */ - int percentage; /* percentage of cell for bar */ - long blinkwait; /* blinking, wait time before blinking starts */ - long blinkon; /* blinking, on time */ - long blinkoff; /* blinking, off time */ - int id; /* highlight group ID */ - int id_lm; /* highlight group ID for :lmap mode */ - char *name; /* mode name (fixed) */ - char used_for; /* SHAPE_MOUSE and/or SHAPE_CURSOR */ + char *full_name; ///< mode description + CursorShape shape; ///< cursor shape: one of the SHAPE_ defines + int mshape; ///< mouse shape: one of the MSHAPE defines + int percentage; ///< percentage of cell for bar + long blinkwait; ///< blinking, wait time before blinking starts + long blinkon; ///< blinking, on time + long blinkoff; ///< blinking, off time + int id; ///< highlight group ID + int id_lm; ///< highlight group ID for :lmap mode + char *name; ///< mode short name + char used_for; ///< SHAPE_MOUSE and/or SHAPE_CURSOR } cursorentry_T; diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 11ade20c1c..ff76abc01f 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1586,7 +1586,7 @@ static int diff_cmp(char_u *s1, char_u *s2) } if ((diff_flags & DIFF_ICASE) && !(diff_flags & DIFF_IWHITE)) { - return mb_stricmp(s1, s2); + return mb_stricmp((const char *)s1, (const char *)s2); } // Ignore white space changes and possibly ignore case. diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 6ba6e659a6..560205fe7d 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1448,10 +1448,8 @@ int get_digraph(int cmdline) { int cc; no_mapping++; - allow_keys++; int c = plain_vgetc(); no_mapping--; - allow_keys--; if (c != ESC) { // ESC cancels CTRL-K @@ -1468,10 +1466,8 @@ int get_digraph(int cmdline) add_to_showcmd(c); } no_mapping++; - allow_keys++; cc = plain_vgetc(); no_mapping--; - allow_keys--; if (cc != ESC) { // ESC cancels CTRL-K diff --git a/src/nvim/edit.c b/src/nvim/edit.c index aa029c38a2..da09aed3dc 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -15,6 +15,7 @@ #include "nvim/cursor.h" #include "nvim/digraph.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/farsi.h" @@ -689,11 +690,9 @@ static int insert_execute(VimState *state, int key) if (s->c == Ctrl_BSL) { // may need to redraw when no more chars available now ins_redraw(false); - ++no_mapping; - ++allow_keys; + no_mapping++; s->c = plain_vgetc(); - --no_mapping; - --allow_keys; + no_mapping--; if (s->c != Ctrl_N && s->c != Ctrl_G && s->c != Ctrl_O) { // it's something else vungetc(s->c); @@ -1424,7 +1423,7 @@ static void ins_ctrl_v(void) edit_putchar('^', TRUE); did_putchar = TRUE; } - AppendToRedobuff((char_u *)CTRL_V_STR); /* CTRL-V */ + AppendToRedobuff(CTRL_V_STR); add_to_showcmd_c(Ctrl_V); @@ -1978,7 +1977,6 @@ static bool ins_compl_accept_char(int c) */ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int dir, int flags) { - char_u *p; int i, c; int actual_len; /* Take multi-byte characters */ int actual_compl_length; /* into account. */ @@ -1988,11 +1986,11 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int int was_letter = FALSE; if (p_ic && curbuf->b_p_inf && len > 0) { - /* Infer case of completed part. */ + // Infer case of completed part. - /* Find actual length of completion. */ + // Find actual length of completion. if (has_mbyte) { - p = str; + const char_u *p = str; actual_len = 0; while (*p != NUL) { mb_ptr_adv(p); @@ -2003,7 +2001,7 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int /* Find actual length of original text. */ if (has_mbyte) { - p = compl_orig_text; + const char_u *p = compl_orig_text; actual_compl_length = 0; while (*p != NUL) { mb_ptr_adv(p); @@ -2019,27 +2017,35 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int /* Allocate wide character array for the completion and fill it. */ wca = xmalloc(actual_len * sizeof(*wca)); - p = str; - for (i = 0; i < actual_len; ++i) - if (has_mbyte) - wca[i] = mb_ptr2char_adv(&p); - else - wca[i] = *(p++); + { + const char_u *p = str; + for (i = 0; i < actual_len; i++) { + if (has_mbyte) { + wca[i] = mb_ptr2char_adv(&p); + } else { + wca[i] = *(p++); + } + } + } - /* Rule 1: Were any chars converted to lower? */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (vim_islower(c)) { - has_lower = TRUE; - if (vim_isupper(wca[i])) { - /* Rule 1 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = vim_tolower(wca[i]); - break; + // Rule 1: Were any chars converted to lower? + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { + c = mb_ptr2char_adv(&p); + } else { + c = *(p++); + } + if (vim_islower(c)) { + has_lower = true; + if (vim_isupper(wca[i])) { + // Rule 1 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { + wca[i] = vim_tolower(wca[i]); + } + break; + } } } } @@ -2049,84 +2055,110 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int * upper case. */ if (!has_lower) { - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { c = mb_ptr2char_adv(&p); - else + } else { c = *(p++); + } if (was_letter && vim_isupper(c) && vim_islower(wca[i])) { - /* Rule 2 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) + // Rule 2 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { wca[i] = vim_toupper(wca[i]); + } break; } was_letter = vim_islower(c) || vim_isupper(c); } } - /* Copy the original case of the part we typed. */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (vim_islower(c)) - wca[i] = vim_tolower(wca[i]); - else if (vim_isupper(c)) - wca[i] = vim_toupper(wca[i]); + // Copy the original case of the part we typed. + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { + c = mb_ptr2char_adv(&p); + } else { + c = *(p++); + } + if (vim_islower(c)) { + wca[i] = vim_tolower(wca[i]); + } else if (vim_isupper(c)) { + wca[i] = vim_toupper(wca[i]); + } + } } - /* - * Generate encoding specific output from wide character array. - * Multi-byte characters can occupy up to five bytes more than - * ASCII characters, and we also need one byte for NUL, so stay - * six bytes away from the edge of IObuff. - */ - p = IObuff; - i = 0; - while (i < actual_len && (p - IObuff + 6) < IOSIZE) - if (has_mbyte) - p += (*mb_char2bytes)(wca[i++], p); - else - *(p++) = wca[i++]; - *p = NUL; + // Generate encoding specific output from wide character array. + // Multi-byte characters can occupy up to five bytes more than + // ASCII characters, and we also need one byte for NUL, so stay + // six bytes away from the edge of IObuff. + { + char_u *p = IObuff; + i = 0; + while (i < actual_len && (p - IObuff + 6) < IOSIZE) { + if (has_mbyte) { + p += (*mb_char2bytes)(wca[i++], p); + } else { + *(p++) = wca[i++]; + } + } + *p = NUL; + } xfree(wca); - return ins_compl_add(IObuff, len, icase, fname, NULL, dir, - flags, FALSE); + return ins_compl_add(IObuff, len, icase, fname, NULL, true, dir, flags, + false); } - return ins_compl_add(str, len, icase, fname, NULL, dir, flags, FALSE); + return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, false); } -/* - * Add a match to the list of matches. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error - * then FAIL is returned. - */ -static int -ins_compl_add ( - char_u *str, - int len, - int icase, - char_u *fname, - char_u **cptext, /* extra text for popup menu or NULL */ - int cdir, - int flags, - int adup /* accept duplicate match */ -) +/// Add a match to the list of matches +/// +/// @param[in] str Match to add. +/// @param[in] len Match length, -1 to use #STRLEN. +/// @param[in] icase Whether case is to be ignored. +/// @param[in] fname File name match comes from. May be NULL. +/// @param[in] cptext Extra text for popup menu. May be NULL. If not NULL, +/// must have exactly #CPT_COUNT items. +/// @param[in] cptext_allocated If true, will not copy cptext strings. +/// +/// @note Will free strings in case of error. +/// cptext itself will not be freed. +/// @param[in] cdir Completion direction. +/// @param[in] adup True if duplicate matches are to be accepted. +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +static int ins_compl_add(char_u *const str, int len, + const bool icase, char_u *const fname, + char_u *const *const cptext, + const bool cptext_allocated, + const Direction cdir, int flags, const bool adup) + FUNC_ATTR_NONNULL_ARG(1) { compl_T *match; - int dir = (cdir == 0 ? compl_direction : cdir); + int dir = (cdir == kDirectionNotSet ? compl_direction : cdir); os_breakcheck(); - if (got_int) +#define FREE_CPTEXT(cptext, cptext_allocated) \ + do { \ + if (cptext_allocated) { \ + for (size_t i = 0; i < CPT_COUNT; i++) { \ + xfree(cptext[i]); \ + } \ + } \ + } while (0) + if (got_int) { + FREE_CPTEXT(cptext, cptext_allocated); return FAIL; - if (len < 0) + } + if (len < 0) { len = (int)STRLEN(str); + } /* * If the same match is already present, don't add it. @@ -2134,10 +2166,12 @@ ins_compl_add ( if (compl_first_match != NULL && !adup) { match = compl_first_match; do { - if ( !(match->cp_flags & ORIGINAL_TEXT) - && STRNCMP(match->cp_str, str, len) == 0 - && match->cp_str[len] == NUL) + if (!(match->cp_flags & ORIGINAL_TEXT) + && STRNCMP(match->cp_str, str, len) == 0 + && match->cp_str[len] == NUL) { + FREE_CPTEXT(cptext, cptext_allocated); return NOTDONE; + } match = match->cp_next; } while (match != NULL && match != compl_first_match); } @@ -2168,16 +2202,26 @@ ins_compl_add ( else if (fname != NULL) { match->cp_fname = vim_strsave(fname); flags |= FREE_FNAME; - } else + } else { match->cp_fname = NULL; + } match->cp_flags = flags; if (cptext != NULL) { int i; - for (i = 0; i < CPT_COUNT; ++i) - if (cptext[i] != NULL && *cptext[i] != NUL) - match->cp_text[i] = vim_strsave(cptext[i]); + for (i = 0; i < CPT_COUNT; i++) { + if (cptext[i] == NULL) { + continue; + } + if (*cptext[i] != NUL) { + match->cp_text[i] = (cptext_allocated + ? cptext[i] + : (char_u *)xstrdup((char *)cptext[i])); + } else if (cptext_allocated) { + xfree(cptext[i]); + } + } } /* @@ -2300,9 +2344,10 @@ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) for (i = 0; i < num_matches && add_r != FAIL; i++) if ((add_r = ins_compl_add(matches[i], -1, icase, - NULL, NULL, dir, 0, FALSE)) == OK) - /* if dir was BACKWARD then honor it just once */ + NULL, NULL, false, dir, 0, false)) == OK) { + // If dir was BACKWARD then honor it just once. dir = FORWARD; + } FreeWild(num_matches, matches); } @@ -2366,8 +2411,8 @@ void set_completion(colnr_T startcol, list_T *list) /* compl_pattern doesn't need to be set */ compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col, compl_length); - if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, 0, - ORIGINAL_TEXT, FALSE) != OK) { + if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, + ORIGINAL_TEXT, false) != OK) { return; } @@ -2889,7 +2934,7 @@ static void ins_compl_clear(void) compl_orig_text = NULL; compl_enter_selects = FALSE; // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); } /// Check that Insert completion is active. @@ -3434,7 +3479,6 @@ expand_by_function ( { list_T *matchlist = NULL; dict_T *matchdict = NULL; - char_u *args[2]; char_u *funcname; pos_T pos; win_T *curwin_save; @@ -3445,9 +3489,8 @@ expand_by_function ( if (*funcname == NUL) return; - /* Call 'completefunc' to obtain the list of matches. */ - args[0] = (char_u *)"0"; - args[1] = base; + // Call 'completefunc' to obtain the list of matches. + const char_u *const args[2] = { (char_u *)"0", base }; pos = curwin->w_cursor; curwin_save = curwin; @@ -3463,8 +3506,8 @@ expand_by_function ( matchdict = rettv.vval.v_dict; break; default: - /* TODO: Give error message? */ - clear_tv(&rettv); + // TODO(brammool): Give error message? + tv_clear(&rettv); break; } } @@ -3486,10 +3529,12 @@ expand_by_function ( ins_compl_add_dict(matchdict); theend: - if (matchdict != NULL) - dict_unref(matchdict); - if (matchlist != NULL) - list_unref(matchlist); + if (matchdict != NULL) { + tv_dict_unref(matchdict); + } + if (matchlist != NULL) { + tv_list_unref(matchlist); + } } /* @@ -3518,53 +3563,60 @@ static void ins_compl_add_dict(dict_T *dict) dictitem_T *di_refresh; dictitem_T *di_words; - /* Check for optional "refresh" item. */ - compl_opt_refresh_always = FALSE; - di_refresh = dict_find(dict, (char_u *)"refresh", 7); + // Check for optional "refresh" item. + compl_opt_refresh_always = false; + di_refresh = tv_dict_find(dict, S_LEN("refresh")); if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) { - char_u *v = di_refresh->di_tv.vval.v_string; + const char *v = (const char *)di_refresh->di_tv.vval.v_string; - if (v != NULL && STRCMP(v, (char_u *)"always") == 0) - compl_opt_refresh_always = TRUE; + if (v != NULL && strcmp(v, "always") == 0) { + compl_opt_refresh_always = true; + } } - /* Add completions from a "words" list. */ - di_words = dict_find(dict, (char_u *)"words", 5); - if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) + // Add completions from a "words" list. + di_words = tv_dict_find(dict, S_LEN("words")); + if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) { ins_compl_add_list(di_words->di_tv.vval.v_list); + } } -/* - * Add a match to the list of matches from a typeval_T. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error - * then FAIL is returned. - */ -int ins_compl_add_tv(typval_T *tv, int dir) -{ - char_u *word; - int icase = FALSE; - int adup = FALSE; - int aempty = FALSE; - char_u *(cptext[CPT_COUNT]); +/// Add a match to the list of matches from VimL object +/// +/// @param[in] tv Object to get matches from. +/// @param[in] dir Completion direction. +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +int ins_compl_add_tv(typval_T *const tv, const Direction dir) + FUNC_ATTR_NONNULL_ALL +{ + const char *word; + bool icase = false; + bool adup = false; + bool aempty = false; + char *(cptext[CPT_COUNT]); if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { - word = get_dict_string(tv->vval.v_dict, "word", false); - cptext[CPT_ABBR] = get_dict_string(tv->vval.v_dict, "abbr", false); - cptext[CPT_MENU] = get_dict_string(tv->vval.v_dict, "menu", false); - cptext[CPT_KIND] = get_dict_string(tv->vval.v_dict, "kind", false); - cptext[CPT_INFO] = get_dict_string(tv->vval.v_dict, "info", false); - - icase = get_dict_number(tv->vval.v_dict, "icase"); - adup = get_dict_number(tv->vval.v_dict, "dup"); - aempty = get_dict_number(tv->vval.v_dict, "empty"); + word = tv_dict_get_string(tv->vval.v_dict, "word", false); + cptext[CPT_ABBR] = tv_dict_get_string(tv->vval.v_dict, "abbr", true); + cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true); + cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true); + cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); + + icase = (bool)tv_dict_get_number(tv->vval.v_dict, "icase"); + adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); + aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); } else { - word = get_tv_string_chk(tv); + word = (const char *)tv_get_string_chk(tv); memset(cptext, 0, sizeof(cptext)); } - if (word == NULL || (!aempty && *word == NUL)) + if (word == NULL || (!aempty && *word == NUL)) { return FAIL; - return ins_compl_add(word, -1, icase, NULL, cptext, dir, 0, adup); + } + return ins_compl_add((char_u *)word, -1, icase, NULL, + (char_u **)cptext, true, dir, 0, adup); } /* @@ -3979,7 +4031,7 @@ static void ins_compl_delete(void) // causes flicker, thus we can't do that. changed_cline_bef_curs(); // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); } // Insert the new text being completed. @@ -3994,17 +4046,21 @@ static void ins_compl_insert(int in_compl_func) // Set completed item. // { word, abbr, menu, kind, info } - dict_T *dict = dict_alloc(); - dict_add_nr_str(dict, "word", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_str)); - dict_add_nr_str(dict, "abbr", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR])); - dict_add_nr_str(dict, "menu", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU])); - dict_add_nr_str(dict, "kind", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND])); - dict_add_nr_str(dict, "info", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); + dict_T *dict = tv_dict_alloc(); + tv_dict_add_str(dict, S_LEN("word"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_str)); + tv_dict_add_str( + dict, S_LEN("abbr"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR])); + tv_dict_add_str( + dict, S_LEN("menu"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU])); + tv_dict_add_str( + dict, S_LEN("kind"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND])); + tv_dict_add_str( + dict, S_LEN("info"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); set_vim_var_dict(VV_COMPLETED_ITEM, dict); if (!in_compl_func) { compl_curr_match = compl_shown_match; @@ -4546,7 +4602,6 @@ static int ins_complete(int c, bool enable_pum) * Call user defined function 'completefunc' with "a:findstart" * set to 1 to obtain the length of text to use for completion. */ - char_u *args[2]; int col; char_u *funcname; pos_T pos; @@ -4565,8 +4620,7 @@ static int ins_complete(int c, bool enable_pum) return FAIL; } - args[0] = (char_u *)"1"; - args[1] = NULL; + const char_u *const args[2] = { (char_u *)"1", NULL }; pos = curwin->w_cursor; curwin_save = curwin; curbuf_save = curbuf; @@ -4666,8 +4720,8 @@ static int ins_complete(int c, bool enable_pum) /* Always add completion for the original text. */ xfree(compl_orig_text); compl_orig_text = vim_strnsave(line + compl_col, compl_length); - if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, 0, - ORIGINAL_TEXT, FALSE) != OK) { + if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, + ORIGINAL_TEXT, false) != OK) { xfree(compl_pattern); compl_pattern = NULL; xfree(compl_orig_text); @@ -5746,15 +5800,16 @@ comp_textwidth ( */ static void redo_literal(int c) { - char_u buf[10]; + char buf[10]; - /* Only digits need special treatment. Translate them into a string of - * three digits. */ + // Only digits need special treatment. Translate them into a string of + // three digits. if (ascii_isdigit(c)) { - vim_snprintf((char *)buf, sizeof(buf), "%03d", c); + vim_snprintf(buf, sizeof(buf), "%03d", c); AppendToRedobuff(buf); - } else + } else { AppendCharToRedobuff(c); + } } // start_arrow() is called when an arrow key is used in insert mode. @@ -5783,8 +5838,8 @@ static void start_arrow_common(pos_T *end_insert_pos, bool end_change) { if (!arrow_used && end_change) { // something has been inserted AppendToRedobuff(ESC_STR); - stop_insert(end_insert_pos, FALSE, FALSE); - arrow_used = TRUE; /* this means we stopped the current insert */ + stop_insert(end_insert_pos, false, false); + arrow_used = true; // This means we stopped the current insert. } check_spell_redraw(); } @@ -5841,7 +5896,7 @@ int stop_arrow(void) vr_lines_changed = 1; } ResetRedobuff(); - AppendToRedobuff((char_u *)"1i"); /* pretend we start an insertion */ + AppendToRedobuff("1i"); // Pretend we start an insertion. new_insert_skip = 2; } else if (ins_need_undo) { if (u_save_cursor() == OK) @@ -6306,12 +6361,13 @@ stuff_inserted ( } do { - stuffReadbuff(ptr); - /* a trailing "0" is inserted as "<C-V>048", "^" as "<C-V>^" */ - if (last) - stuffReadbuff((char_u *)(last == '0' - ? "\026\060\064\070" - : "\026^")); + stuffReadbuff((const char *)ptr); + // A trailing "0" is inserted as "<C-V>048", "^" as "<C-V>^". + if (last) { + stuffReadbuff((last == '0' + ? "\026\060\064\070" + : "\026^")); + } } while (--count > 0); if (last) @@ -7068,8 +7124,8 @@ static void ins_ctrl_g(void) */ static void ins_ctrl_hat(void) { - if (map_to_exists_mode((char_u *)"", LANGMAP, FALSE)) { - /* ":lmap" mappings exists, Toggle use of ":lmap" mappings. */ + if (map_to_exists_mode("", LANGMAP, false)) { + // ":lmap" mappings exists, Toggle use of ":lmap" mappings. if (State & LANGMAP) { curbuf->b_p_iminsert = B_IMODE_NONE; State &= ~LANGMAP; @@ -7104,13 +7160,12 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) disabled_redraw = false; } if (!arrow_used) { - /* - * Don't append the ESC for "r<CR>" and "grx". - * When 'insertmode' is set only CTRL-L stops Insert mode. Needed for - * when "count" is non-zero. - */ - if (cmdchar != 'r' && cmdchar != 'v') - AppendToRedobuff(p_im ? (char_u *)"\014" : ESC_STR); + // Don't append the ESC for "r<CR>" and "grx". + // When 'insertmode' is set only CTRL-L stops Insert mode. Needed for + // when "count" is non-zero. + if (cmdchar != 'r' && cmdchar != 'v') { + AppendToRedobuff(p_im ? "\014" : ESC_STR); + } /* * Repeating insert may take a long time. Check for @@ -7264,7 +7319,8 @@ static bool ins_start_select(int c) // Execute the key in (insert) Select mode. stuffcharReadbuff(Ctrl_O); if (mod_mask) { - char_u buf[4] = { K_SPECIAL, KS_MODIFIER, mod_mask, NUL }; + const char buf[] = { (char)K_SPECIAL, (char)KS_MODIFIER, + (char)(uint8_t)mod_mask, NUL }; stuffReadbuff(buf); } stuffcharReadbuff(c); @@ -8072,11 +8128,11 @@ static bool ins_tab(void) return true; } - did_ai = FALSE; - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - AppendToRedobuff((char_u *)"\t"); + did_ai = false; + did_si = false; + can_si = false; + can_si_back = false; + AppendToRedobuff("\t"); if (p_sta && ind) { // insert tab in indent, use "shiftwidth" temp = get_sw_value(curbuf); @@ -8303,17 +8359,16 @@ static int ins_digraph(void) } - /* don't map the digraph chars. This also prevents the - * mode message to be deleted when ESC is hit */ - ++no_mapping; - ++allow_keys; + // don't map the digraph chars. This also prevents the + // mode message to be deleted when ESC is hit + no_mapping++; c = plain_vgetc(); - --no_mapping; - --allow_keys; - if (did_putchar) - /* when the line fits in 'columns' the '?' is at the start of the next - * line and will not be removed by the redraw */ + no_mapping--; + if (did_putchar) { + // when the line fits in 'columns' the '?' is at the start of the next + // line and will not be removed by the redraw edit_unputchar(); + } if (IS_SPECIAL(c) || mod_mask) { /* special key */ clear_showcmd(); @@ -8333,18 +8388,17 @@ static int ins_digraph(void) } add_to_showcmd_c(c); } - ++no_mapping; - ++allow_keys; + no_mapping++; cc = plain_vgetc(); - --no_mapping; - --allow_keys; - if (did_putchar) - /* when the line fits in 'columns' the '?' is at the start of the - * next line and will not be removed by a redraw */ + no_mapping--; + if (did_putchar) { + // when the line fits in 'columns' the '?' is at the start of the + // next line and will not be removed by a redraw edit_unputchar(); + } if (cc != ESC) { - AppendToRedobuff((char_u *)CTRL_V_STR); - c = getdigraph(c, cc, TRUE); + AppendToRedobuff(CTRL_V_STR); + c = getdigraph(c, cc, true); clear_showcmd(); return c; } @@ -8406,12 +8460,13 @@ static int ins_ctrl_ey(int tc) if (c != NUL) { long tw_save; - /* The character must be taken literally, insert like it - * was typed after a CTRL-V, and pretend 'textwidth' - * wasn't set. Digits, 'o' and 'x' are special after a - * CTRL-V, don't use it for these. */ - if (c < 256 && !isalnum(c)) - AppendToRedobuff((char_u *)CTRL_V_STR); /* CTRL-V */ + // The character must be taken literally, insert like it + // was typed after a CTRL-V, and pretend 'textwidth' + // wasn't set. Digits, 'o' and 'x' are special after a + // CTRL-V, don't use it for these. + if (c < 256 && !isalnum(c)) { + AppendToRedobuff(CTRL_V_STR); + } tw_save = curbuf->b_p_tw; curbuf->b_p_tw = -1; insert_special(c, TRUE, FALSE); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 062999e73b..6a18baf2e2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -94,7 +94,12 @@ #include "nvim/lib/kvec.h" #include "nvim/lib/khash.h" #include "nvim/lib/queue.h" -#include "nvim/eval/typval_encode.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/executor.h" +#include "nvim/eval/gc.h" +#include "nvim/macros.h" + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ @@ -132,31 +137,28 @@ * "newkey" is the key for the new item. */ typedef struct lval_S { - char_u *ll_name; /* start of variable name (can be NULL) */ - char_u *ll_exp_name; /* NULL or expanded name in allocated memory. */ - typval_T *ll_tv; /* Typeval of item being used. If "newkey" - isn't NULL it's the Dict to which to add - the item. */ - listitem_T *ll_li; /* The list item or NULL. */ - list_T *ll_list; /* The list or NULL. */ - int ll_range; /* TRUE when a [i:j] range was used */ - long ll_n1; /* First index for list */ - long ll_n2; /* Second index for list range */ - int ll_empty2; /* Second index is empty: [i:] */ - dict_T *ll_dict; /* The Dictionary or NULL */ - dictitem_T *ll_di; /* The dictitem or NULL */ - char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ + const char *ll_name; ///< Start of variable name (can be NULL). + size_t ll_name_len; ///< Length of the .ll_name. + char *ll_exp_name; ///< NULL or expanded name in allocated memory. + typval_T *ll_tv; ///< Typeval of item being used. If "newkey" + ///< isn't NULL it's the Dict to which to add the item. + listitem_T *ll_li; ///< The list item or NULL. + list_T *ll_list; ///< The list or NULL. + int ll_range; ///< TRUE when a [i:j] range was used. + long ll_n1; ///< First index for list. + long ll_n2; ///< Second index for list range. + int ll_empty2; ///< Second index is empty: [i:]. + dict_T *ll_dict; ///< The Dictionary or NULL. + dictitem_T *ll_di; ///< The dictitem or NULL. + char_u *ll_newkey; ///< New key for Dict in allocated memory or NULL. } lval_T; static char *e_letunexp = N_("E18: Unexpected characters in :let"); -static char *e_listidx = N_("E684: list index out of range: %" PRId64); -static char *e_undefvar = N_("E121: Undefined variable: %s"); static char *e_missbrac = N_("E111: Missing ']'"); static char *e_listarg = N_("E686: Argument of %s must be a List"); static char *e_listdictarg = N_( "E712: Argument of %s must be a List or Dictionary"); -static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary"); static char *e_listreq = N_("E714: List required"); static char *e_dictreq = N_("E715: Dictionary required"); static char *e_stringreq = N_("E928: String required"); @@ -167,17 +169,20 @@ static char *e_funcexts = N_( static char *e_funcdict = N_("E717: Dictionary entry already exists"); static char *e_funcref = N_("E718: Funcref required"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); -static char *e_letwrong = N_("E734: Wrong variable type for %s="); static char *e_nofunc = N_("E130: Unknown function: %s"); static char *e_illvar = N_("E461: Illegal variable name: %s"); -static char *e_float_as_string = N_("E806: using Float as a String"); static const char *e_readonlyvar = N_( "E46: Cannot change read-only variable \"%.*s\""); -static char_u * const empty_string = (char_u *)""; +// TODO(ZyX-I): move to eval/executor +static char *e_letwrong = N_("E734: Wrong variable type for %s="); + static char_u * const namespace_char = (char_u *)"abglstvw"; -static dictitem_T globvars_var; /* variable used for g: */ +/// Variable used for g: +static ScopeDictDictItem globvars_var; + +/// g: value #define globvarht globvardict.dv_hashtab /* @@ -188,12 +193,15 @@ static hashtab_T compat_hashtab; hashtab_T func_hashtab; +// Used for checking if local variables or arguments used in a lambda. +static int *eval_lavars_used = NULL; + /* * Array to hold the hashtab with variables local to each sourced script. * Each item holds a variable (nameless) that points to the dict_T. */ typedef struct { - dictitem_T sv_var; + ScopeDictDictItem sv_var; dict_T sv_dict; } scriptvar_T; @@ -231,17 +239,44 @@ typedef enum { // The names of packages that once were loaded are remembered. static garray_T ga_loaded = { 0, 0, sizeof(char_u *), 4, NULL }; -// List heads for garbage collection. Although there can be a reference loop -// from partial to dict to partial, we don't need to keep track of the partial, -// since it will get freed when the dict is unused and gets freed. -static dict_T *first_dict = NULL; // list of all dicts -static list_T *first_list = NULL; // list of all lists - -#define FLEN_FIXED 40 - #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] +/// Short variable name length +#define VAR_SHORT_LEN 20 +/// Number of fixed variables used for arguments +#define FIXVAR_CNT 12 + +struct funccall_S { + ufunc_T *func; ///< Function being called. + int linenr; ///< Next line to be executed. + int returned; ///< ":return" used. + /// Fixed variables for arguments. + TV_DICTITEM_STRUCT(VAR_SHORT_LEN + 1) fixvar[FIXVAR_CNT]; + dict_T l_vars; ///< l: local function variables. + ScopeDictDictItem l_vars_var; ///< Variable for l: scope. + dict_T l_avars; ///< a: argument variables. + ScopeDictDictItem l_avars_var; ///< Variable for a: scope. + list_T l_varlist; ///< List for a:000. + listitem_T l_listitems[MAX_FUNC_ARGS]; ///< List items for a:000. + typval_T *rettv; ///< Return value. + linenr_T breakpoint; ///< Next line with breakpoint or zero. + int dbg_tick; ///< Debug_tick when breakpoint was set. + int level; ///< Top nesting level of executed function. + proftime_T prof_child; ///< Time spent in a child. + funccall_T *caller; ///< Calling function or NULL. + int fc_refcount; ///< Number of user functions that reference this funccall. + int fc_copyID; ///< CopyID used for garbage collection. + garray_T fc_funcs; ///< List of ufunc_T* which keep a reference to "func". +}; + +///< Structure used by trans_function_name() +typedef struct { + dict_T *fd_dict; ///< Dictionary used. + char_u *fd_newkey; ///< New key in "dict" in allocated memory. + dictitem_T *fd_di; ///< Dictionary item used. +} funcdict_T; + /* * Info used by a ":for" loop. */ @@ -283,8 +318,8 @@ typedef enum { // variables with the VV_ defines. static struct vimvar { char *vv_name; ///< Name of the variable, without v:. - dictitem16_T vv_di; ///< Value and name for key (max 16 chars) - char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. + TV_DICTITEM_STRUCT(17) vv_di; ///< Value and name for key (max 16 chars). + char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. } vimvars[] = { // VV_ tails differing from upcased string literals: @@ -389,23 +424,11 @@ static struct vimvar { #define vv_dict vv_di.di_tv.vval.v_dict #define vv_tv vv_di.di_tv -static dictitem_T vimvars_var; // variable used for v: -#define vimvarht vimvardict.dv_hashtab +/// Variable used for v: +static ScopeDictDictItem vimvars_var; -typedef enum { - kCallbackNone, - kCallbackFuncref, - kCallbackPartial, -} CallbackType; - -typedef struct { - union { - char_u *funcref; - partial_T *partial; - } data; - CallbackType type; -} Callback; -#define CALLBACK_NONE ((Callback){ .type = kCallbackNone }) +/// v: hashtab +#define vimvarht vimvardict.dv_hashtab typedef struct { union { @@ -424,13 +447,6 @@ typedef struct { MultiQueue *events; } TerminalJobData; -typedef struct dict_watcher { - Callback callback; - char *key_pattern; - QUEUE node; - bool busy; // prevent recursion if the dict is changed in the callback -} DictWatcher; - typedef struct { TerminalJobData *data; Callback *callback; @@ -466,6 +482,24 @@ typedef struct fst { KHASH_MAP_INIT_STR(functions, VimLFuncDef) +/// Type of assert_* check being performed +typedef enum +{ + ASSERT_EQUAL, + ASSERT_NOTEQUAL, + ASSERT_MATCH, + ASSERT_NOTMATCH, + ASSERT_INRANGE, + ASSERT_OTHER, +} assert_type_T; + +/// Type for dict_list function +typedef enum { + kDictListKeys, ///< List dictionary keys. + kDictListValues, ///< List dictionary values. + kDictListItems, ///< List dictionary contents: [keys, values]. +} DictListType; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.c.generated.h" #endif @@ -546,19 +580,19 @@ void eval_init(void) } vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; - dict_T *const msgpack_types_dict = dict_alloc(); + dict_T *const msgpack_types_dict = tv_dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { - list_T *const type_list = list_alloc(); + list_T *const type_list = tv_list_alloc(); type_list->lv_lock = VAR_FIXED; type_list->lv_refcount = 1; - dictitem_T *const di = dictitem_alloc((char_u *)msgpack_type_names[i]); + dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]); di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX; di->di_tv = (typval_T) { .v_type = VAR_LIST, .vval = { .v_list = type_list, }, }; eval_msgpack_type_lists[i] = type_list; - if (dict_add(msgpack_types_dict, di) == FAIL) { + if (tv_dict_add(msgpack_types_dict, di) == FAIL) { // There must not be duplicate items in this dictionary by definition. assert(false); } @@ -566,12 +600,12 @@ void eval_init(void) msgpack_types_dict->dv_lock = VAR_FIXED; set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict); - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); - dict_T *v_event = dict_alloc(); + dict_T *v_event = tv_dict_alloc(); v_event->dv_lock = VAR_FIXED; set_vim_var_dict(VV_EVENT, v_event); - set_vim_var_list(VV_ERRORS, list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc()); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); set_vim_var_nr(VV_COUNT1, 1); @@ -602,7 +636,7 @@ void eval_clear(void) xfree(p->vv_str); p->vv_str = NULL; } else if (p->vv_di.di_tv.v_type == VAR_LIST) { - list_unref(p->vv_list); + tv_list_unref(p->vv_list); p->vv_list = NULL; } } @@ -690,18 +724,17 @@ int current_func_returned(void) */ void set_internal_string_var(char_u *name, char_u *value) { - char_u *val = vim_strsave(value); - typval_T *tvp = xcalloc(1, sizeof(typval_T)); + const typval_T tv = { + .v_type = VAR_STRING, + .vval.v_string = value, + }; - tvp->v_type = VAR_STRING; - tvp->vval.v_string = val; - set_var(name, tvp, FALSE); - free_tv(tvp); + set_var((const char *)name, STRLEN(name), (typval_T *)&tv, true); } static lval_T *redir_lval = NULL; -static garray_T redir_ga; /* only valid when redir_lval is not NULL */ -static char_u *redir_endp = NULL; +static garray_T redir_ga; // Only valid when redir_lval is not NULL. +static char_u *redir_endp = NULL; static char_u *redir_varname = NULL; /* @@ -732,11 +765,11 @@ var_redir_start ( /* The output is stored in growarray "redir_ga" until redirection ends. */ ga_init(&redir_ga, (int)sizeof(char), 500); - /* Parse the variable name (can be a dict or list entry). */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0, - FNE_CHECK_START); - if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != - NUL) { + // Parse the variable name (can be a dict or list entry). + redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, false, false, + 0, FNE_CHECK_START); + if (redir_endp == NULL || redir_lval->ll_name == NULL + || *redir_endp != NUL) { clear_lval(redir_lval); if (redir_endp != NULL && *redir_endp != NUL) /* Trailing characters are present after the variable name */ @@ -810,12 +843,13 @@ void var_redir_stop(void) ga_append(&redir_ga, NUL); /* Append the trailing NUL. */ tv.v_type = VAR_STRING; tv.vval.v_string = redir_ga.ga_data; - /* Call get_lval() again, if it's inside a Dict or List it may - * have changed. */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, - FALSE, FALSE, 0, FNE_CHECK_START); - if (redir_endp != NULL && redir_lval->ll_name != NULL) - set_var_lval(redir_lval, redir_endp, &tv, FALSE, (char_u *)"."); + // Call get_lval() again, if it's inside a Dict or List it may + // have changed. + redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, + false, false, 0, FNE_CHECK_START); + if (redir_endp != NULL && redir_lval->ll_name != NULL) { + set_var_lval(redir_lval, redir_endp, &tv, false, (char_u *)"."); + } clear_lval(redir_lval); } @@ -833,7 +867,7 @@ void var_redir_stop(void) int eval_charconvert(const char *const enc_from, const char *const enc_to, const char *const fname_from, const char *const fname_to) { - int err = false; + bool err = false; set_vim_var_string(VV_CC_FROM, enc_from, -1); set_vim_var_string(VV_CC_TO, enc_to, -1); @@ -855,7 +889,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to, int eval_printexpr(const char *const fname, const char *const args) { - int err = false; + bool err = false; set_vim_var_string(VV_FNAME_IN, fname, -1); set_vim_var_string(VV_CMDARG, args, -1); @@ -875,7 +909,7 @@ int eval_printexpr(const char *const fname, const char *const args) void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile) { - int err = FALSE; + bool err = false; set_vim_var_string(VV_FNAME_IN, origfile, -1); set_vim_var_string(VV_FNAME_NEW, newfile, -1); @@ -889,7 +923,7 @@ void eval_diff(const char *const origfile, const char *const newfile, void eval_patch(const char *const origfile, const char *const difffile, const char *const outfile) { - int err; + bool err = false; set_vim_var_string(VV_FNAME_IN, origfile, -1); set_vim_var_string(VV_FNAME_DIFF, difffile, -1); @@ -908,56 +942,61 @@ void eval_patch(const char *const origfile, const char *const difffile, int eval_to_bool ( char_u *arg, - int *error, + bool *error, char_u **nextcmd, int skip /* only parse, don't execute */ ) { typval_T tv; - int retval = FALSE; + bool retval = false; - if (skip) - ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL) - *error = TRUE; - else { - *error = FALSE; + if (skip) { + emsg_skip++; + } + if (eval0(arg, &tv, nextcmd, !skip) == FAIL) { + *error = true; + } else { + *error = false; if (!skip) { - retval = (get_tv_number_chk(&tv, error) != 0); - clear_tv(&tv); + retval = (tv_get_number_chk(&tv, error) != 0); + tv_clear(&tv); } } - if (skip) - --emsg_skip; + if (skip) { + emsg_skip--; + } return retval; } -/* - * Top level evaluation function, returning a string. If "skip" is TRUE, - * only parsing to "nextcmd" is done, without reporting errors. Return - * pointer to allocated memory, or NULL for failure or when "skip" is TRUE. - */ -char_u * -eval_to_string_skip ( - char_u *arg, - char_u **nextcmd, - int skip /* only parse, don't execute */ -) +/// Top level evaluation function, returning a string +/// +/// @param[in] arg String to evaluate. +/// @param nextcmd Pointer to the start of the next Ex command. +/// @param[in] skip If true, only do parsing to nextcmd without reporting +/// errors or actually evaluating anything. +/// +/// @return [allocated] string result of evaluation or NULL in case of error or +/// when skipping. +char *eval_to_string_skip(const char *arg, const char **nextcmd, + const bool skip) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT { typval_T tv; - char_u *retval; + char *retval; - if (skip) - ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL || skip) + if (skip) { + emsg_skip++; + } + if (eval0((char_u *)arg, &tv, (char_u **)nextcmd, !skip) == FAIL || skip) { retval = NULL; - else { - retval = vim_strsave(get_tv_string(&tv)); - clear_tv(&tv); + } else { + retval = xstrdup(tv_get_string(&tv)); + tv_clear(&tv); + } + if (skip) { + emsg_skip--; } - if (skip) - --emsg_skip; return retval; } @@ -983,31 +1022,33 @@ int skip_expr(char_u **pp) char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) { typval_T tv; - char_u *retval; + char *retval; garray_T ga; - char_u numbuf[NUMBUFLEN]; - if (eval0(arg, &tv, nextcmd, TRUE) == FAIL) + if (eval0(arg, &tv, nextcmd, true) == FAIL) { retval = NULL; - else { + } else { if (convert && tv.v_type == VAR_LIST) { ga_init(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { - list_join(&ga, tv.vval.v_list, "\n"); - if (tv.vval.v_list->lv_len > 0) + tv_list_join(&ga, tv.vval.v_list, "\n"); + if (tv.vval.v_list->lv_len > 0) { ga_append(&ga, NL); + } } ga_append(&ga, NUL); - retval = (char_u *)ga.ga_data; + retval = (char *)ga.ga_data; } else if (convert && tv.v_type == VAR_FLOAT) { - vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv.vval.v_float); - retval = vim_strsave(numbuf); - } else - retval = vim_strsave(get_tv_string(&tv)); - clear_tv(&tv); + char numbuf[NUMBUFLEN]; + vim_snprintf(numbuf, NUMBUFLEN, "%g", tv.vval.v_float); + retval = xstrdup(numbuf); + } else { + retval = xstrdup(tv_get_string(&tv)); + } + tv_clear(&tv); } - return retval; + return (char_u *)retval; } /* @@ -1044,11 +1085,11 @@ int eval_to_number(char_u *expr) ++emsg_off; - if (eval1(&p, &rettv, TRUE) == FAIL) + if (eval1(&p, &rettv, true) == FAIL) { retval = -1; - else { - retval = get_tv_number_chk(&rettv, NULL); - clear_tv(&rettv); + } else { + retval = tv_get_number_chk(&rettv, NULL); + tv_clear(&rettv); } --emsg_off; @@ -1105,11 +1146,12 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) if (p_verbose == 0) ++emsg_off; - if (eval1(&p, &rettv, TRUE) == OK) { - if (rettv.v_type != VAR_LIST) - clear_tv(&rettv); - else + if (eval1(&p, &rettv, true) == OK) { + if (rettv.v_type != VAR_LIST) { + tv_clear(&rettv); + } else { list = rettv.vval.v_list; + } } if (p_verbose == 0) @@ -1125,36 +1167,21 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) * Return -1 if anything isn't right. * Used to get the good word and score from the eval_spell_expr() result. */ -int get_spellword(list_T *list, char_u **pp) +int get_spellword(list_T *list, const char **pp) { listitem_T *li; li = list->lv_first; - if (li == NULL) + if (li == NULL) { return -1; - *pp = get_tv_string(&li->li_tv); + } + *pp = tv_get_string(&li->li_tv); li = li->li_next; - if (li == NULL) + if (li == NULL) { return -1; - return get_tv_number(&li->li_tv); -} - -/* - * Top level evaluation function. - * Returns an allocated typval_T with the result. - * Returns NULL when there is an error. - */ -typval_T *eval_expr(char_u *arg, char_u **nextcmd) -{ - typval_T *tv = xmalloc(sizeof(typval_T)); - - if (eval0(arg, tv, nextcmd, TRUE) == FAIL) { - xfree(tv); - return NULL; } - - return tv; + return tv_get_number(&li->li_tv); } @@ -1164,9 +1191,9 @@ typval_T *eval_expr(char_u *arg, char_u **nextcmd) // // Return OK or FAIL. int call_vim_function( - char_u *func, + const char_u *func, int argc, - char_u **argv, + const char_u *const *const argv, int safe, // use the sandbox int str_arg_only, // all arguments are strings typval_T *rettv @@ -1199,7 +1226,7 @@ int call_vim_function( argvars[i].vval.v_number = n; } else { argvars[i].v_type = VAR_STRING; - argvars[i].vval.v_string = argv[i]; + argvars[i].vval.v_string = (char_u *)argv[i]; } } @@ -1208,7 +1235,7 @@ int call_vim_function( ++sandbox; } - rettv->v_type = VAR_UNKNOWN; // clear_tv() uses this + rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &doesrange, true, NULL, NULL); @@ -1219,7 +1246,7 @@ int call_vim_function( xfree(argvars); if (ret == FAIL) { - clear_tv(rettv); + tv_clear(rettv); } return ret; @@ -1234,8 +1261,8 @@ long call_func_retnr ( char_u *func, int argc, - char_u **argv, - int safe /* use the sandbox */ + const char_u *const *const argv, + int safe // use the sandbox ) { typval_T rettv; @@ -1245,33 +1272,34 @@ call_func_retnr ( if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL) return -1; - retval = get_tv_number_chk(&rettv, NULL); - clear_tv(&rettv); + retval = tv_get_number_chk(&rettv, NULL); + tv_clear(&rettv); return retval; } -/* - * Call vimL function "func" and return the result as a string. - * Returns NULL when calling the function fails. - * Uses argv[argc] for the function arguments. - */ -void * -call_func_retstr ( - char_u *func, - int argc, - char_u **argv, - int safe /* use the sandbox */ -) +/// Call VimL function and return the result as a string +/// +/// @param[in] func Function name. +/// @param[in] argc Number of arguments. +/// @param[in] argv Array with string arguments. +/// @param[in] safe Use the sandbox. +/// +/// @return [allocated] NULL when calling function failes, allocated string +/// otherwise. +char *call_func_retstr(const char *const func, const int argc, + const char_u *const *const argv, + const bool safe) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { typval_T rettv; - char_u *retval; - - /* All arguments are passed as strings, no conversion to number. */ - if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL) + // All arguments are passed as strings, no conversion to number. + if (call_vim_function((const char_u *)func, argc, argv, safe, true, &rettv) + == FAIL) { return NULL; + } - retval = vim_strsave(get_tv_string(&rettv)); - clear_tv(&rettv); + char *const retval = xstrdup(tv_get_string(&rettv)); + tv_clear(&rettv); return retval; } @@ -1284,8 +1312,8 @@ void * call_func_retlist ( char_u *func, int argc, - char_u **argv, - int safe /* use the sandbox */ + const char_u *const *const argv, + int safe // use the sandbox ) { typval_T rettv; @@ -1295,7 +1323,7 @@ call_func_retlist ( return NULL; if (rettv.v_type != VAR_LIST) { - clear_tv(&rettv); + tv_clear(&rettv); return NULL; } @@ -1393,7 +1421,7 @@ int eval_foldexpr(char_u *arg, int *cp) *cp = *s++; retval = atol((char *)s); } - clear_tv(&tv); + tv_clear(&tv); } --emsg_off; if (use_sandbox) @@ -1424,11 +1452,13 @@ void ex_let(exarg_T *eap) char_u *argend; int first = TRUE; - argend = skip_var_list(arg, &var_count, &semicolon); - if (argend == NULL) + argend = (char_u *)skip_var_list(arg, &var_count, &semicolon); + if (argend == NULL) { return; - if (argend > arg && argend[-1] == '.') /* for var.='str' */ - --argend; + } + if (argend > arg && argend[-1] == '.') { // For var.='str'. + argend--; + } expr = skipwhite(argend); if (*expr != '=' && !(vim_strchr((char_u *)"+-.", *expr) != NULL && expr[1] == '=')) { @@ -1465,13 +1495,13 @@ void ex_let(exarg_T *eap) ++emsg_skip; i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); if (eap->skip) { - if (i != FAIL) - clear_tv(&rettv); - --emsg_skip; + if (i != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; } else if (i != FAIL) { - (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count, - op); - clear_tv(&rettv); + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, op); + tv_clear(&rettv); } } } @@ -1494,7 +1524,7 @@ ex_let_vars ( char_u *nextchars ) { - char_u *arg = arg_start; + char_u *arg = arg_start; list_T *l; int i; listitem_T *item; @@ -1517,7 +1547,7 @@ ex_let_vars ( return FAIL; } - i = list_len(l); + i = tv_list_len(l); if (semicolon == 0 && var_count < i) { EMSG(_("E687: Less targets than List items")); return FAIL; @@ -1539,9 +1569,9 @@ ex_let_vars ( if (*arg == ';') { /* Put the rest of the list (may be empty) in the var after ';'. * Create a new list for this. */ - l = list_alloc(); + l = tv_list_alloc(); while (item != NULL) { - list_append_tv(l, &item->li_tv); + tv_list_append_tv(l, &item->li_tv); item = item->li_next; } @@ -1550,11 +1580,12 @@ ex_let_vars ( ltv.vval.v_list = l; l->lv_refcount = 1; - arg = ex_let_one(skipwhite(arg + 1), <v, FALSE, - (char_u *)"]", nextchars); - clear_tv(<v); - if (arg == NULL) + arg = ex_let_one(skipwhite(arg + 1), <v, false, + (char_u *)"]", nextchars); + tv_clear(<v); + if (arg == NULL) { return FAIL; + } break; } else if (*arg != ',' && *arg != ']') { EMSG2(_(e_intern2), "ex_let_vars()"); @@ -1572,9 +1603,11 @@ ex_let_vars ( * for "[var, var; var]" set "semicolon". * Return NULL for an error. */ -static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon) +static const char_u *skip_var_list(const char_u *arg, int *var_count, + int *semicolon) { - char_u *p, *s; + const char_u *p; + const char_u *s; if (*arg == '[') { /* "[var, var]": find the matching ']'. */ @@ -1611,7 +1644,7 @@ static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon) * Skip one (assignable) variable name, including @r, $VAR, &option, d.key, * l[idx]. */ -static char_u *skip_var_one(char_u *arg) +static const char_u *skip_var_one(const char_u *arg) { if (*arg == '@' && arg[1] != NUL) return arg + 2; @@ -1634,7 +1667,7 @@ static void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) { if (!HASHITEM_EMPTY(hi)) { todo--; - di = HI2DI(hi); + di = TV_DICT_HI2DI(hi); if (empty || di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string != NULL) { list_one_var(di, prefix, first); @@ -1775,7 +1808,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) tv.v_type, s == NULL ? "" : s, first); xfree(s); } - clear_tv(&tv); + tv_clear(&tv); } } } @@ -1789,34 +1822,37 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) return arg; } -/* - * Set one item of ":let var = expr" or ":let [v1, v2] = list" to its value. - * Returns a pointer to the char just after the var name. - * Returns NULL if there is an error. - */ -static char_u * -ex_let_one ( - char_u *arg, /* points to variable name */ - typval_T *tv, /* value to assign to variable */ - int copy, /* copy value from "tv" */ - char_u *endchars, /* valid chars after variable name or NULL */ - char_u *op /* "+", "-", "." or NULL*/ -) -{ - char_u *name; - char_u *arg_end = NULL; +// TODO(ZyX-I): move to eval/ex_cmds + +/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value +/// +/// @param[in] arg Start of the variable name. +/// @param[in] tv Value to assign to the variable. +/// @param[in] copy If true, copy value from `tv`. +/// @param[in] endchars Valid characters after variable name or NULL. +/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc. +/// NULL for `=`. +/// +/// @return a pointer to the char just after the var name or NULL in case of +/// error. +static char_u *ex_let_one(char_u *arg, typval_T *const tv, + const bool copy, const char_u *const endchars, + const char_u *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + char_u *arg_end = NULL; int len; int opt_flags; - char_u *tofree = NULL; + char_u *tofree = NULL; /* * ":let $VAR = expr": Set environment variable. */ if (*arg == '$') { - /* Find the end of the name. */ - ++arg; - name = arg; - len = get_env_len(&arg); + // Find the end of the name. + arg++; + char *name = (char *)arg; + len = get_env_len((const char_u **)&arg); if (len == 0) { EMSG2(_(e_invarg2), name - 1); } else { @@ -1826,26 +1862,28 @@ ex_let_one ( && vim_strchr(endchars, *skipwhite(arg)) == NULL) { EMSG(_(e_letunexp)); } else if (!check_secure()) { - const char_u c1 = name[len]; + const char c1 = name[len]; name[len] = NUL; - char_u *p = get_tv_string_chk(tv); + const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { - char *s = vim_getenv((char *)name); + char *s = vim_getenv(name); if (s != NULL) { - p = tofree = concat_str((char_u *)s, p); + tofree = concat_str((const char_u *)s, (const char_u *)p); + p = (const char *)tofree; xfree(s); } } if (p != NULL) { - vim_setenv((char *)name, (char *)p); - if (STRICMP(name, "HOME") == 0) + vim_setenv(name, p); + if (STRICMP(name, "HOME") == 0) { init_homedir(); - else if (didset_vim && STRICMP(name, "VIM") == 0) - didset_vim = FALSE; - else if (didset_vimruntime - && STRICMP(name, "VIMRUNTIME") == 0) - didset_vimruntime = FALSE; + } else if (didset_vim && STRICMP(name, "VIM") == 0) { + didset_vim = false; + } else if (didset_vimruntime + && STRICMP(name, "VIMRUNTIME") == 0) { + didset_vimruntime = false; + } arg_end = arg; } name[len] = c1; @@ -1863,19 +1901,18 @@ ex_let_one ( && vim_strchr(endchars, *skipwhite((const char_u *)p)) == NULL)) { EMSG(_(e_letunexp)); } else { - long n; int opt_type; long numval; - char_u *stringval = NULL; - char_u *s; + char *stringval = NULL; const char c1 = *p; *p = NUL; - n = get_tv_number(tv); - s = get_tv_string_chk(tv); /* != NULL if number or string */ + varnumber_T n = tv_get_number(tv); + const char *s = tv_get_string_chk(tv); // != NULL if number or string. if (s != NULL && op != NULL && *op != '=') { - opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + opt_type = get_option_value(arg, &numval, (char_u **)&stringval, + opt_flags); if ((opt_type == 1 && *op == '.') || (opt_type == 0 && *op != '.')) { EMSG2(_(e_letwrong), op); @@ -1887,44 +1924,45 @@ ex_let_one ( n = numval - n; } } else if (opt_type == 0 && stringval != NULL) { // string - s = concat_str(stringval, s); - xfree(stringval); - stringval = s; + char *const oldstringval = stringval; + stringval = (char *)concat_str((const char_u *)stringval, + (const char_u *)s); + xfree(oldstringval); + s = stringval; } } } if (s != NULL) { - set_option_value(arg, n, s, opt_flags); + set_option_value((const char *)arg, n, s, opt_flags); arg_end = (char_u *)p; } *p = c1; xfree(stringval); } - } - /* - * ":let @r = expr": Set register contents. - */ - else if (*arg == '@') { - ++arg; - if (op != NULL && (*op == '+' || *op == '-')) - EMSG2(_(e_letwrong), op); - else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) - EMSG(_(e_letunexp)); - else { - char_u *ptofree = NULL; + // ":let @r = expr": Set register contents. + } else if (*arg == '@') { + arg++; + if (op != NULL && (*op == '+' || *op == '-')) { + emsgf(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) { + emsgf(_(e_letunexp)); + } else { char_u *s; - char_u *p = get_tv_string_chk(tv); + char_u *ptofree = NULL; + const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); if (s != NULL) { - p = ptofree = concat_str(s, p); + ptofree = concat_str(s, (const char_u *)p); + p = (const char *)ptofree; xfree(s); } } if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, p, STRLEN(p), false); + write_reg_contents(*arg == '@' ? '"' : *arg, + (const char_u *)p, STRLEN(p), false); arg_end = arg + 1; } xfree(ptofree); @@ -1939,9 +1977,9 @@ ex_let_one ( char_u *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); if (p != NULL && lv.ll_name != NULL) { - if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) + if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { EMSG(_(e_letunexp)); - else { + } else { set_var_lval(&lv, p, tv, copy, op); arg_end = p; } @@ -1953,6 +1991,8 @@ ex_let_one ( return arg_end; } +// TODO(ZyX-I): move to eval/executor + /// Get an lvalue /// /// Lvalue may be @@ -1981,16 +2021,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, const int flags, const int fne_flags) FUNC_ATTR_NONNULL_ARG(1, 3) { - char_u *p; - char_u *expr_start, *expr_end; - int cc; dictitem_T *v; typval_T var1; typval_T var2; int empty1 = FALSE; listitem_T *ni; - char_u *key = NULL; - int len; hashtab_T *ht; int quiet = flags & GLV_QUIET; @@ -1998,13 +2033,19 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, memset(lp, 0, sizeof(lval_T)); if (skip) { - /* When skipping just find the end of the name. */ - lp->ll_name = name; - return find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); + // When skipping just find the end of the name. + lp->ll_name = (const char *)name; + return (char_u *)find_name_end((const char_u *)name, NULL, NULL, + FNE_INCL_BR | fne_flags); } - /* Find the end of the name. */ - p = find_name_end(name, &expr_start, &expr_end, fne_flags); + // Find the end of the name. + char_u *expr_start; + char_u *expr_end; + char_u *p = (char_u *)find_name_end(name, + (const char_u **)&expr_start, + (const char_u **)&expr_end, + fne_flags); if (expr_start != NULL) { /* Don't expand the name when we already know there is an error. */ if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p) @@ -2013,7 +2054,9 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } - lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p); + lp->ll_exp_name = (char *)make_expanded_name(name, expr_start, expr_end, + (char_u *)p); + lp->ll_name = lp->ll_exp_name; if (lp->ll_exp_name == NULL) { /* Report an invalid expression in braces, unless the * expression evaluation has been cancelled due to an @@ -2023,25 +2066,28 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, EMSG2(_(e_invarg2), name); return NULL; } + lp->ll_name_len = 0; + } else { + lp->ll_name_len = strlen(lp->ll_name); } - lp->ll_name = lp->ll_exp_name; - } else - lp->ll_name = name; + } else { + lp->ll_name = (const char *)name; + lp->ll_name_len = (size_t)((const char *)p - lp->ll_name); + } - /* Without [idx] or .key we are done. */ - if ((*p != '[' && *p != '.') || lp->ll_name == NULL) + // Without [idx] or .key we are done. + if ((*p != '[' && *p != '.') || lp->ll_name == NULL) { return p; + } - cc = *p; - *p = NUL; - v = find_var((const char *)lp->ll_name, STRLEN(lp->ll_name), &ht, - flags & GLV_NO_AUTOLOAD); + v = find_var(lp->ll_name, lp->ll_name_len, &ht, flags & GLV_NO_AUTOLOAD); if (v == NULL && !quiet) { - EMSG2(_(e_undefvar), lp->ll_name); + emsgf(_("E121: Undefined variable: %.*s"), + (int)lp->ll_name_len, lp->ll_name); } - *p = cc; - if (v == NULL) + if (v == NULL) { return NULL; + } /* * Loop until no more [idx] or .key is following. @@ -2061,29 +2107,32 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } - len = -1; + int len = -1; + char_u *key = NULL; if (*p == '.') { key = p + 1; - for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) - ; + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) { + } if (len == 0) { - if (!quiet) - EMSG(_(e_emptykey)); + if (!quiet) { + EMSG(_("E713: Cannot use empty key after .")); + } return NULL; } p = key + len; } else { /* Get the index [expr] or the first index [expr: ]. */ p = skipwhite(p + 1); - if (*p == ':') - empty1 = TRUE; - else { - empty1 = FALSE; - if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */ + if (*p == ':') { + empty1 = true; + } else { + empty1 = false; + if (eval1(&p, &var1, true) == FAIL) { // Recursive! return NULL; - if (get_tv_string_chk(&var1) == NULL) { - /* not a number or string */ - clear_tv(&var1); + } + if (!tv_check_str(&var1)) { + // Not a number or string. + tv_clear(&var1); return NULL; } } @@ -2091,35 +2140,41 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, /* Optionally get the second index [ :expr]. */ if (*p == ':') { if (lp->ll_tv->v_type == VAR_DICT) { - if (!quiet) + if (!quiet) { EMSG(_(e_dictrange)); - if (!empty1) - clear_tv(&var1); + } + if (!empty1) { + tv_clear(&var1); + } return NULL; } if (rettv != NULL && (rettv->v_type != VAR_LIST || rettv->vval.v_list == NULL)) { - if (!quiet) - EMSG(_("E709: [:] requires a List value")); - if (!empty1) - clear_tv(&var1); + if (!quiet) { + emsgf(_("E709: [:] requires a List value")); + } + if (!empty1) { + tv_clear(&var1); + } return NULL; } p = skipwhite(p + 1); - if (*p == ']') - lp->ll_empty2 = TRUE; - else { - lp->ll_empty2 = FALSE; - if (eval1(&p, &var2, TRUE) == FAIL) { /* recursive! */ - if (!empty1) - clear_tv(&var1); + if (*p == ']') { + lp->ll_empty2 = true; + } else { + lp->ll_empty2 = false; + if (eval1(&p, &var2, true) == FAIL) { // Recursive! + if (!empty1) { + tv_clear(&var1); + } return NULL; } - if (get_tv_string_chk(&var2) == NULL) { - /* not a number or string */ - if (!empty1) - clear_tv(&var1); - clear_tv(&var2); + if (!tv_check_str(&var2)) { + // Not a number or string. + if (!empty1) { + tv_clear(&var1); + } + tv_clear(&var2); return NULL; } } @@ -2128,12 +2183,15 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, lp->ll_range = FALSE; if (*p != ']') { - if (!quiet) - EMSG(_(e_missbrac)); - if (!empty1) - clear_tv(&var1); - if (lp->ll_range && !lp->ll_empty2) - clear_tv(&var2); + if (!quiet) { + emsgf(_(e_missbrac)); + } + if (!empty1) { + tv_clear(&var1); + } + if (lp->ll_range && !lp->ll_empty2) { + tv_clear(&var2); + } return NULL; } @@ -2144,15 +2202,15 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (lp->ll_tv->v_type == VAR_DICT) { if (len == -1) { // "[key]": get key from "var1" - key = get_tv_string_chk(&var1); // is number or string + key = (char_u *)tv_get_string(&var1); // is number or string if (key == NULL) { - clear_tv(&var1); + tv_clear(&var1); return NULL; } } lp->ll_list = NULL; lp->ll_dict = lp->ll_tv->vval.v_dict; - lp->ll_di = dict_find(lp->ll_dict, key, len); + lp->ll_di = tv_dict_find(lp->ll_dict, (const char *)key, len); /* When assigning to a scope dictionary check that a function and * variable name is valid (only variable name unless it is l: or @@ -2164,16 +2222,19 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (len != -1) { prevval = key[len]; key[len] = NUL; - } else - prevval = 0; /* avoid compiler warning */ - wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE - && rettv->v_type == VAR_FUNC - && var_check_func_name(key, lp->ll_di == NULL)) - || !valid_varname(key); - if (len != -1) + } else { + prevval = 0; // Avoid compiler warning. + } + wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE + && tv_is_func(*rettv) + && !var_check_func_name((const char *)key, lp->ll_di == NULL)) + || !valid_varname((const char *)key)); + if (len != -1) { key[len] = prevval; - if (wrong) + } + if (wrong) { return NULL; + } } if (lp->ll_di == NULL) { @@ -2185,56 +2246,61 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, /* Key does not exist in dict: may need to add it. */ if (*p == '[' || *p == '.' || unlet) { - if (!quiet) - EMSG2(_(e_dictkey), key); - if (len == -1) - clear_tv(&var1); + if (!quiet) { + emsgf(_(e_dictkey), key); + } + if (len == -1) { + tv_clear(&var1); + } return NULL; } - if (len == -1) + if (len == -1) { lp->ll_newkey = vim_strsave(key); - else + } else { lp->ll_newkey = vim_strnsave(key, len); - if (len == -1) - clear_tv(&var1); + } + if (len == -1) { + tv_clear(&var1); + } break; // existing variable, need to check if it can be changed } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags, (const char *)name, (size_t)(p - name))) { if (len == -1) { - clear_tv(&var1); + tv_clear(&var1); } return NULL; } - if (len == -1) - clear_tv(&var1); + if (len == -1) { + tv_clear(&var1); + } lp->ll_tv = &lp->ll_di->di_tv; } else { - /* - * Get the number and item for the only or first index of the List. - */ - if (empty1) + // Get the number and item for the only or first index of the List. + if (empty1) { lp->ll_n1 = 0; - else { - lp->ll_n1 = get_tv_number(&var1); /* is number or string */ - clear_tv(&var1); + } else { + lp->ll_n1 = tv_get_number(&var1); // Is number or string. + tv_clear(&var1); } lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = list_find(lp->ll_list, lp->ll_n1); + lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); if (lp->ll_li == NULL) { if (lp->ll_n1 < 0) { lp->ll_n1 = 0; - lp->ll_li = list_find(lp->ll_list, lp->ll_n1); + lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); } } if (lp->ll_li == NULL) { - if (lp->ll_range && !lp->ll_empty2) - clear_tv(&var2); - if (!quiet) + if (lp->ll_range && !lp->ll_empty2) { + tv_clear(&var2); + } + if (!quiet) { EMSGN(_(e_listidx), lp->ll_n1); + } return NULL; } @@ -2245,24 +2311,26 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, * Otherwise "lp->ll_n2" is set to the second index. */ if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = get_tv_number(&var2); /* is number or string */ - clear_tv(&var2); + lp->ll_n2 = tv_get_number(&var2); // Is number or string. + tv_clear(&var2); if (lp->ll_n2 < 0) { - ni = list_find(lp->ll_list, lp->ll_n2); + ni = tv_list_find(lp->ll_list, lp->ll_n2); if (ni == NULL) { if (!quiet) EMSGN(_(e_listidx), lp->ll_n2); return NULL; } - lp->ll_n2 = list_idx_of_item(lp->ll_list, ni); + lp->ll_n2 = tv_list_idx_of_item(lp->ll_list, ni); } - /* Check that lp->ll_n2 isn't before lp->ll_n1. */ - if (lp->ll_n1 < 0) - lp->ll_n1 = list_idx_of_item(lp->ll_list, lp->ll_li); + // Check that lp->ll_n2 isn't before lp->ll_n1. + if (lp->ll_n1 < 0) { + lp->ll_n1 = tv_list_idx_of_item(lp->ll_list, lp->ll_li); + } if (lp->ll_n2 < lp->ll_n1) { - if (!quiet) + if (!quiet) { EMSGN(_(e_listidx), lp->ll_n2); + } return NULL; } } @@ -2274,6 +2342,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return p; } +// TODO(ZyX-I): move to eval/executor + /* * Clear lval "lp" that was filled by get_lval(). */ @@ -2283,12 +2353,15 @@ static void clear_lval(lval_T *lp) xfree(lp->ll_newkey); } +// TODO(ZyX-I): move to eval/executor + /* * Set a variable that was parsed by get_lval() to "rettv". * "endp" points to just after the parsed name. * "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=". */ -static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, char_u *op) +static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, + int copy, const char_u *op) { int cc; listitem_T *ri; @@ -2309,13 +2382,13 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch STRLEN(lp->ll_name)) && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, STRLEN(lp->ll_name)))) - && tv_op(&tv, rettv, op) == OK) { - set_var(lp->ll_name, &tv, false); + && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { + set_var(lp->ll_name, lp->ll_name_len, &tv, false); } - clear_tv(&tv); + tv_clear(&tv); } } else { - set_var(lp->ll_name, rettv, copy); + set_var(lp->ll_name, lp->ll_name_len, rettv, copy); } *endp = cc; } else if (tv_check_lock(lp->ll_newkey == NULL @@ -2345,18 +2418,18 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch * Assign the List values to the list items. */ for (ri = rettv->vval.v_list->lv_first; ri != NULL; ) { - if (op != NULL && *op != '=') - tv_op(&lp->ll_li->li_tv, &ri->li_tv, op); - else { - clear_tv(&lp->ll_li->li_tv); - copy_tv(&ri->li_tv, &lp->ll_li->li_tv); + if (op != NULL && *op != '=') { + eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op); + } else { + tv_clear(&lp->ll_li->li_tv); + tv_copy(&ri->li_tv, &lp->ll_li->li_tv); } ri = ri->li_next; if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) break; if (lp->ll_li->li_next == NULL) { - /* Need to add an empty item. */ - list_append_number(lp->ll_list, 0); + // Need to add an empty item. + tv_list_append_number(lp->ll_list, 0); assert(lp->ll_li->li_next); } lp->ll_li = lp->ll_li->li_next; @@ -2369,198 +2442,60 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch : lp->ll_n1 != lp->ll_n2) EMSG(_("E711: List value has not enough items")); } else { - typval_T oldtv; + typval_T oldtv = TV_INITIAL_VALUE; dict_T *dict = lp->ll_dict; - bool watched = is_watched(dict); + bool watched = tv_dict_is_watched(dict); - if (watched) { - init_tv(&oldtv); - } - - /* - * Assign to a List or Dictionary item. - */ + // Assign to a List or Dictionary item. if (lp->ll_newkey != NULL) { if (op != NULL && *op != '=') { EMSG2(_(e_letwrong), op); return; } - /* Need to add an item to the Dictionary. */ - di = dictitem_alloc(lp->ll_newkey); - if (dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { + // Need to add an item to the Dictionary. + di = tv_dict_item_alloc((const char *)lp->ll_newkey); + if (tv_dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { xfree(di); return; } lp->ll_tv = &di->di_tv; } else { if (watched) { - copy_tv(lp->ll_tv, &oldtv); + tv_copy(lp->ll_tv, &oldtv); } if (op != NULL && *op != '=') { - tv_op(lp->ll_tv, rettv, op); + eexe_mod_op(lp->ll_tv, rettv, (const char *)op); goto notify; } else { - clear_tv(lp->ll_tv); + tv_clear(lp->ll_tv); } } // Assign the value to the variable or list item. if (copy) { - copy_tv(rettv, lp->ll_tv); + tv_copy(rettv, lp->ll_tv); } else { *lp->ll_tv = *rettv; lp->ll_tv->v_lock = 0; - init_tv(rettv); + tv_init(rettv); } notify: if (watched) { if (oldtv.v_type == VAR_UNKNOWN) { - dictwatcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); + tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); } else { dictitem_T *di = lp->ll_di; - dictwatcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); - clear_tv(&oldtv); + tv_dict_watcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); + tv_clear(&oldtv); } } } } -/* - * Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2" - * Returns OK or FAIL. - */ -static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op) -{ - long n; - char_u numbuf[NUMBUFLEN]; - char_u *s; - - // Can't do anything with a Funcref, a Dict or special value on the right. - if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { - switch (tv1->v_type) { - case VAR_DICT: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_SPECIAL: - break; - - case VAR_LIST: - if (*op != '+' || tv2->v_type != VAR_LIST) - break; - /* List += List */ - if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) - list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); - return OK; - - case VAR_NUMBER: - case VAR_STRING: - if (tv2->v_type == VAR_LIST) - break; - if (*op == '+' || *op == '-') { - /* nr += nr or nr -= nr*/ - n = get_tv_number(tv1); - if (tv2->v_type == VAR_FLOAT) { - float_T f = n; - - if (*op == '+') - f += tv2->vval.v_float; - else - f -= tv2->vval.v_float; - clear_tv(tv1); - tv1->v_type = VAR_FLOAT; - tv1->vval.v_float = f; - } else { - if (*op == '+') - n += get_tv_number(tv2); - else - n -= get_tv_number(tv2); - clear_tv(tv1); - tv1->v_type = VAR_NUMBER; - tv1->vval.v_number = n; - } - } else { - if (tv2->v_type == VAR_FLOAT) - break; - - /* str .= str */ - s = get_tv_string(tv1); - s = concat_str(s, get_tv_string_buf(tv2, numbuf)); - clear_tv(tv1); - tv1->v_type = VAR_STRING; - tv1->vval.v_string = s; - } - return OK; - - case VAR_FLOAT: - { - float_T f; - - if (*op == '.' || (tv2->v_type != VAR_FLOAT - && tv2->v_type != VAR_NUMBER - && tv2->v_type != VAR_STRING)) - break; - if (tv2->v_type == VAR_FLOAT) - f = tv2->vval.v_float; - else - f = get_tv_number(tv2); - if (*op == '+') - tv1->vval.v_float += f; - else - tv1->vval.v_float -= f; - } - return OK; - - case VAR_UNKNOWN: - assert(false); - } - } - - EMSG2(_(e_letwrong), op); - return FAIL; -} - -/* - * Add a watcher to a list. - */ -void list_add_watch(list_T *l, listwatch_T *lw) -{ - lw->lw_next = l->lv_watch; - l->lv_watch = lw; -} - -/* - * Remove a watcher from a list. - * No warning when it isn't found... - */ -void list_rem_watch(list_T *l, listwatch_T *lwrem) -{ - listwatch_T *lw, **lwp; - - lwp = &l->lv_watch; - for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { - if (lw == lwrem) { - *lwp = lw->lw_next; - break; - } - lwp = &lw->lw_next; - } -} - -/* - * Just before removing an item from a list: advance watchers to the next - * item. - */ -static void list_fix_watch(list_T *l, listitem_T *item) -{ - listwatch_T *lw; - - for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next) - if (lw->lw_item == item) - lw->lw_item = item->li_next; -} +// TODO(ZyX-I): move to eval/ex_cmds /* * Evaluate the expression used in a ":for var in expr" command. @@ -2568,14 +2503,14 @@ static void list_fix_watch(list_T *l, listitem_T *item) * Set "*errp" to TRUE for an error, FALSE otherwise; * Return a pointer that holds the info. Null when there is an error. */ -void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) +void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); - char_u *expr; + const char_u *expr; typval_T tv; list_T *l; - *errp = TRUE; /* default: there is an error */ + *errp = true; // Default: there is an error. expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); if (expr == NULL) @@ -2590,20 +2525,20 @@ void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) if (skip) ++emsg_skip; if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) { - *errp = FALSE; + *errp = false; if (!skip) { l = tv.vval.v_list; if (tv.v_type != VAR_LIST) { EMSG(_(e_listreq)); - clear_tv(&tv); + tv_clear(&tv); } else if (l == NULL) { // a null list is like an empty list: do nothing - clear_tv(&tv); + tv_clear(&tv); } else { /* No need to increment the refcount, it's already set for the * list being used in "tv". */ fi->fi_list = l; - list_add_watch(l, &fi->fi_lw); + tv_list_watch_add(l, &fi->fi_lw); fi->fi_lw.lw_item = l->lv_first; } } @@ -2614,6 +2549,8 @@ void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) return fi; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Use the first item in a ":for" list. Advance to the next. * Assign the values to the variable (list). "arg" points to the first one. @@ -2637,6 +2574,8 @@ int next_for_item(void *fi_void, char_u *arg) return result; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Free the structure used to store info used by ":for". */ @@ -2645,8 +2584,8 @@ void free_for_info(void *fi_void) forinfo_T *fi = (forinfo_T *)fi_void; if (fi != NULL && fi->fi_list != NULL) { - list_rem_watch(fi->fi_list, &fi->fi_lw); - list_unref(fi->fi_list); + tv_list_watch_remove(fi->fi_list, &fi->fi_lw); + tv_list_unref(fi->fi_list); } xfree(fi); } @@ -2734,6 +2673,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) xp->xp_pattern = arg; } +// TODO(ZyX-I): move to eval/ex_cmds /* * ":1,25call func(arg1, arg2)" function call. @@ -2753,13 +2693,14 @@ void ex_call(exarg_T *eap) partial_T *partial = NULL; if (eap->skip) { - /* trans_function_name() doesn't work well when skipping, use eval0() - * instead to skip to any following command, e.g. for: - * :if 0 | call dict.foo().bar() | endif */ - ++emsg_skip; - if (eval0(eap->arg, &rettv, &eap->nextcmd, FALSE) != FAIL) - clear_tv(&rettv); - --emsg_skip; + // trans_function_name() doesn't work well when skipping, use eval0() + // instead to skip to any following command, e.g. for: + // :if 0 | call dict.foo().bar() | endif. + emsg_skip++; + if (eval0(eap->arg, &rettv, &eap->nextcmd, false) != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; return; } @@ -2787,7 +2728,7 @@ void ex_call(exarg_T *eap) /* Skip white space to allow ":call func ()". Not good, but required for * backward compatibility. */ startarg = skipwhite(arg); - rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ + rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this. if (*startarg != '(') { EMSG2(_("E107: Missing parentheses: %s"), eap->arg); @@ -2826,9 +2767,10 @@ void ex_call(exarg_T *eap) break; } - clear_tv(&rettv); - if (doesrange || eap->skip) + tv_clear(&rettv); + if (doesrange || eap->skip) { break; + } /* Stop when immediately aborting on error, or when an interrupt * occurred or an exception was thrown but not caught. @@ -2850,10 +2792,12 @@ void ex_call(exarg_T *eap) } end: - dict_unref(fudi.fd_dict); + tv_dict_unref(fudi.fd_dict); xfree(tofree); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":unlet[!] var1 ... " command. */ @@ -2862,6 +2806,8 @@ void ex_unlet(exarg_T *eap) ex_unletlock(eap, eap->arg, 0); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":lockvar" and ":unlockvar" commands */ @@ -2880,22 +2826,25 @@ void ex_lockvar(exarg_T *eap) ex_unletlock(eap, arg, deep); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":unlet", ":lockvar" and ":unlockvar" are quite similar. */ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) { char_u *arg = argstart; - char_u *name_end; - int error = FALSE; + bool error = false; lval_T lv; do { - /* Parse the name and find the end. */ - name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, 0, - FNE_CHECK_START); - if (lv.ll_name == NULL) - error = TRUE; /* error but continue parsing */ + // Parse the name and find the end. + char_u *const name_end = (char_u *)get_lval(arg, NULL, &lv, true, + eap->skip || error, + 0, FNE_CHECK_START); + if (lv.ll_name == NULL) { + error = true; // error, but continue parsing. + } if (name_end == NULL || (!ascii_iswhite(*name_end) && !ends_excmd(*name_end))) { if (name_end != NULL) { @@ -2913,8 +2862,9 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) error = TRUE; } else { if (do_lock_var(&lv, name_end, deep, - eap->cmdidx == CMD_lockvar) == FAIL) - error = TRUE; + eap->cmdidx == CMD_lockvar) == FAIL) { + error = true; + } } } @@ -2927,7 +2877,9 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) eap->nextcmd = check_nextcmd(arg); } -static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) +// TODO(ZyX-I): move to eval/ex_cmds + +static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) { int ret = OK; int cc; @@ -2937,17 +2889,17 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) *name_end = NUL; // Normal name or expanded name. - if (do_unlet(lp->ll_name, forceit) == FAIL) { + if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) { ret = FAIL; } *name_end = cc; } else if ((lp->ll_list != NULL && tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name))) + lp->ll_name_len)) || (lp->ll_dict != NULL && tv_check_lock(lp->ll_dict->dv_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name)))) { + lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { listitem_T *li; @@ -2957,7 +2909,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name))) { + lp->ll_name_len)) { return false; } ll_li = li; @@ -2967,33 +2919,33 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) /* Delete a range of List items. */ while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { li = lp->ll_li->li_next; - listitem_remove(lp->ll_list, lp->ll_li); + tv_list_item_remove(lp->ll_list, lp->ll_li); lp->ll_li = li; ++lp->ll_n1; } } else { if (lp->ll_list != NULL) { // unlet a List item. - listitem_remove(lp->ll_list, lp->ll_li); + tv_list_item_remove(lp->ll_list, lp->ll_li); } else { // unlet a Dictionary item. dict_T *d = lp->ll_dict; dictitem_T *di = lp->ll_di; - bool watched = is_watched(d); + bool watched = tv_dict_is_watched(d); char *key = NULL; typval_T oldtv; if (watched) { - copy_tv(&di->di_tv, &oldtv); + tv_copy(&di->di_tv, &oldtv); // need to save key because dictitem_remove will free it key = xstrdup((char *)di->di_key); } - dictitem_remove(d, di); + tv_dict_item_remove(d, di); if (watched) { - dictwatcher_notify(d, key, NULL, &oldtv); - clear_tv(&oldtv); + tv_dict_watcher_notify(d, key, NULL, &oldtv); + tv_clear(&oldtv); xfree(key); } } @@ -3002,22 +2954,24 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) return ret; } -/* - * "unlet" a variable. Return OK if it existed, FAIL if not. - * When "forceit" is TRUE don't complain if the variable doesn't exist. - */ -int do_unlet(char_u *name, int forceit) +// TODO(ZyX-I): move to eval/ex_cmds + +/// unlet a variable +/// +/// @param[in] name Variable name to unlet. +/// @param[in] name_len Variable name length. +/// @param[in] fonceit If true, do not complain if variable doesn’t exist. +/// +/// @return OK if it existed, FAIL otherwise. +int do_unlet(const char *const name, const size_t name_len, const int forceit) + FUNC_ATTR_NONNULL_ALL { - hashtab_T *ht; - hashitem_T *hi; - char_u *varname; - dict_T *d; - dictitem_T *di; + const char *varname; dict_T *dict; - ht = find_var_ht_dict((const char *)name, STRLEN(name), - (const char **)&varname, &dict); + hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict); if (ht != NULL && *varname != NUL) { + dict_T *d; if (ht == &globvarht) { d = &globvardict; } else if (current_funccal != NULL @@ -3026,19 +2980,19 @@ int do_unlet(char_u *name, int forceit) } else if (ht == &compat_hashtab) { d = &vimvardict; } else { - di = find_var_in_ht(ht, *name, "", 0, false); + dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false); d = di->di_tv.vval.v_dict; } if (d == NULL) { EMSG2(_(e_intern2), "do_unlet()"); return FAIL; } - hi = hash_find(ht, varname); + hashitem_T *hi = hash_find(ht, (const char_u *)varname); if (HASHITEM_EMPTY(hi)) { hi = find_hi_in_scoped_ht((const char *)name, &ht); } if (hi != NULL && !HASHITEM_EMPTY(hi)) { - di = HI2DI(hi); + dictitem_T *const di = TV_DICT_HI2DI(hi); if (var_check_fixed(di->di_flags, (const char *)name, STRLEN(name)) || var_check_ro(di->di_flags, (const char *)name, STRLEN(name)) || tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) { @@ -3051,17 +3005,17 @@ int do_unlet(char_u *name, int forceit) } typval_T oldtv; - bool watched = is_watched(dict); + bool watched = tv_dict_is_watched(dict); if (watched) { - copy_tv(&di->di_tv, &oldtv); + tv_copy(&di->di_tv, &oldtv); } delete_var(ht, hi); if (watched) { - dictwatcher_notify(dict, (char *)varname, NULL, &oldtv); - clear_tv(&oldtv); + tv_dict_watcher_notify(dict, varname, NULL, &oldtv); + tv_clear(&oldtv); } return OK; } @@ -3072,12 +3026,15 @@ int do_unlet(char_u *name, int forceit) return FAIL; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Lock or unlock variable indicated by "lp". * "deep" is the levels to go (-1 for unlimited); * "lock" is TRUE for ":lockvar", FALSE for ":unlockvar". */ -static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) +static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep, + const bool lock) { int ret = OK; @@ -3087,9 +3044,8 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) if (lp->ll_tv == NULL) { // Normal name or expanded name. - const size_t name_len = (size_t)(name_end - lp->ll_name); dictitem_T *const di = find_var( - (const char *)lp->ll_name, name_len, NULL, + (const char *)lp->ll_name, lp->ll_name_len, NULL, true); if (di == NULL) { ret = FAIL; @@ -3105,134 +3061,39 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) } else { di->di_flags &= ~DI_FLAGS_LOCK; } - item_lock(&di->di_tv, deep, lock); + tv_item_lock(&di->di_tv, deep, lock); } } else if (lp->ll_range) { listitem_T *li = lp->ll_li; /* (un)lock a range of List items. */ while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - item_lock(&li->li_tv, deep, lock); + tv_item_lock(&li->li_tv, deep, lock); li = li->li_next; ++lp->ll_n1; } } else if (lp->ll_list != NULL) { // (un)lock a List item. - item_lock(&lp->ll_li->li_tv, deep, lock); + tv_item_lock(&lp->ll_li->li_tv, deep, lock); } else { // (un)lock a Dictionary item. - item_lock(&lp->ll_di->di_tv, deep, lock); + tv_item_lock(&lp->ll_di->di_tv, deep, lock); } return ret; } /* - * Lock or unlock an item. "deep" is nr of levels to go. - */ -static void item_lock(typval_T *tv, int deep, int lock) -{ - static int recurse = 0; - - if (recurse >= DICT_MAXNEST) { - EMSG(_("E743: variable nested too deep for (un)lock")); - return; - } - if (deep == 0) { - return; - } - recurse++; - - // lock/unlock the item itself -#define CHANGE_LOCK(var, lock) \ - do { \ - var = ((VarLockStatus[]) { \ - [VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ - [VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ - [VAR_FIXED] = VAR_FIXED, \ - })[var]; \ - } while (0) - CHANGE_LOCK(tv->v_lock, lock); - - switch (tv->v_type) { - case VAR_LIST: { - list_T *const l = tv->vval.v_list; - if (l != NULL) { - CHANGE_LOCK(l->lv_lock, lock); - if (deep < 0 || deep > 1) { - // Recursive: lock/unlock the items the List contains. - for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { - item_lock(&li->li_tv, deep - 1, lock); - } - } - } - break; - } - case VAR_DICT: { - dict_T *const d = tv->vval.v_dict; - if (d != NULL) { - CHANGE_LOCK(d->dv_lock, lock); - if (deep < 0 || deep > 1) { - // Recursive: lock/unlock the items the List contains. - int todo = (int)d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); - } - } - } - } - break; - } - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_STRING: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_SPECIAL: { - break; - } - case VAR_UNKNOWN: { - assert(false); - } - } -#undef CHANGE_LOCK - recurse--; -} - -/* - * Return TRUE if typeval "tv" is locked: Either that value is locked itself - * or it refers to a List or Dictionary that is locked. - */ -static int tv_islocked(typval_T *tv) -{ - return (tv->v_lock & VAR_LOCKED) - || (tv->v_type == VAR_LIST - && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock & VAR_LOCKED)) - || (tv->v_type == VAR_DICT - && tv->vval.v_dict != NULL - && (tv->vval.v_dict->dv_lock & VAR_LOCKED)); -} - -/* * Delete all "menutrans_" variables. */ void del_menutrans_vars(void) { - hashitem_T *hi; - int todo; - hash_lock(&globvarht); - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0) - delete_var(&globvarht, hi); + HASHTAB_ITER(&globvarht, hi, { + if (STRNCMP(hi->hi_key, "menutrans_", 10) == 0) { + delete_var(&globvarht, hi); } - } + }); hash_unlock(&globvarht); } @@ -3344,6 +3205,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) return NULL; } +// TODO(ZyX-I): move to eval/expressions /// Return TRUE if "pat" matches "text". /// Does not use 'cpo' and always uses 'magic'. @@ -3380,6 +3242,8 @@ typedef enum { , TYPE_NOMATCH /* !~ */ } exptype_T; +// TODO(ZyX-I): move to eval/expressions + /* * The "evaluate" argument: When FALSE, the argument is only parsed but not * executed. The function may return OK, but the rettv will be of type @@ -3393,7 +3257,7 @@ typedef enum { * Note: "rettv.v_lock" is not set. * Return OK or FAIL. */ -static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) +int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) { int ret; char_u *p; @@ -3401,15 +3265,15 @@ static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) p = skipwhite(arg); ret = eval1(&p, rettv, evaluate); if (ret == FAIL || !ends_excmd(*p)) { - if (ret != FAIL) - clear_tv(rettv); - /* - * Report the invalid expression unless the expression evaluation has - * been cancelled due to an aborting error, an interrupt, or an - * exception. - */ - if (!aborting()) - EMSG2(_(e_invexpr2), arg); + if (ret != FAIL) { + tv_clear(rettv); + } + // Report the invalid expression unless the expression evaluation has + // been cancelled due to an aborting error, an interrupt, or an + // exception. + if (!aborting()) { + emsgf(_(e_invexpr2), arg); + } ret = FAIL; } if (nextcmd != NULL) @@ -3418,6 +3282,8 @@ static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) return ret; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle top level expression: * expr2 ? expr1 : expr1 @@ -3443,13 +3309,15 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) if ((*arg)[0] == '?') { result = FALSE; if (evaluate) { - int error = FALSE; + bool error = false; - if (get_tv_number_chk(rettv, &error) != 0) - result = TRUE; - clear_tv(rettv); - if (error) + if (tv_get_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { return FAIL; + } } /* @@ -3463,9 +3331,10 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) * Check for the ":". */ if ((*arg)[0] != ':') { - EMSG(_("E109: Missing ':' after '?'")); - if (evaluate && result) - clear_tv(rettv); + emsgf(_("E109: Missing ':' after '?'")); + if (evaluate && result) { + tv_clear(rettv); + } return FAIL; } @@ -3473,9 +3342,10 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) * Get the third variable. */ *arg = skipwhite(*arg + 1); - if (eval1(arg, &var2, evaluate && !result) == FAIL) { /* recursive! */ - if (evaluate && result) - clear_tv(rettv); + if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive! + if (evaluate && result) { + tv_clear(rettv); + } return FAIL; } if (evaluate && !result) @@ -3485,6 +3355,8 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle first level expression: * expr2 || expr2 || expr2 logical OR @@ -3499,7 +3371,7 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) typval_T var2; long result; int first; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -3514,12 +3386,14 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) result = FALSE; while ((*arg)[0] == '|' && (*arg)[1] == '|') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) != 0) - result = TRUE; - clear_tv(rettv); - if (error) + if (tv_get_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { return FAIL; - first = FALSE; + } + first = false; } /* @@ -3533,11 +3407,13 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && !result) { - if (get_tv_number_chk(&var2, &error) != 0) - result = TRUE; - clear_tv(&var2); - if (error) + if (tv_get_number_chk(&var2, &error) != 0) { + result = true; + } + tv_clear(&var2); + if (error) { return FAIL; + } } if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -3548,6 +3424,8 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle second level expression: * expr3 && expr3 && expr3 logical AND @@ -3562,7 +3440,7 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) typval_T var2; long result; int first; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -3577,12 +3455,14 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) result = TRUE; while ((*arg)[0] == '&' && (*arg)[1] == '&') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) == 0) - result = FALSE; - clear_tv(rettv); - if (error) + if (tv_get_number_chk(rettv, &error) == 0) { + result = false; + } + tv_clear(rettv); + if (error) { return FAIL; - first = FALSE; + } + first = false; } /* @@ -3596,11 +3476,13 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && result) { - if (get_tv_number_chk(&var2, &error) == 0) - result = FALSE; - clear_tv(&var2); - if (error) + if (tv_get_number_chk(&var2, &error) == 0) { + result = false; + } + tv_clear(&var2); + if (error) { return FAIL; + } } if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -3611,6 +3493,8 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle third level expression: * var1 == var2 @@ -3638,8 +3522,6 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) int type_is = FALSE; /* TRUE for "is" and "isnot" */ int len = 2; long n1, n2; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; int ic; /* @@ -3707,7 +3589,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) */ *arg = skipwhite(p + len); if (eval5(arg, &var2, evaluate) == FAIL) { - clear_tv(rettv); + tv_clear(rettv); return FAIL; } @@ -3729,15 +3611,15 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) } else { EMSG(_("E692: Invalid operation for List")); } - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } else { - /* Compare two Lists for being equal or unequal. */ - n1 = list_equal(rettv->vval.v_list, var2.vval.v_list, - ic, FALSE); - if (type == TYPE_NEQUAL) + // Compare two Lists for being equal or unequal. + n1 = tv_list_equal(rettv->vval.v_list, var2.vval.v_list, ic, false); + if (type == TYPE_NEQUAL) { n1 = !n1; + } } } else if (rettv->v_type == VAR_DICT || var2.v_type == VAR_DICT) { if (type_is) { @@ -3751,23 +3633,22 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) EMSG(_("E735: Can only compare Dictionary with Dictionary")); else EMSG(_("E736: Invalid operation for Dictionary")); - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } else { - /* Compare two Dictionaries for being equal or unequal. */ - n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict, - ic, FALSE); - if (type == TYPE_NEQUAL) + // Compare two Dictionaries for being equal or unequal. + n1 = tv_dict_equal(rettv->vval.v_dict, var2.vval.v_dict, + ic, false); + if (type == TYPE_NEQUAL) { n1 = !n1; + } } - } else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL - || var2.v_type == VAR_PARTIAL) { + } else if (tv_is_func(*rettv) || tv_is_func(var2)) { if (type != TYPE_EQUAL && type != TYPE_NEQUAL) { EMSG(_("E694: Invalid operation for Funcrefs")); - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } if ((rettv->v_type == VAR_PARTIAL @@ -3802,25 +3683,27 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) && type != TYPE_MATCH && type != TYPE_NOMATCH) { float_T f1, f2; - if (rettv->v_type == VAR_FLOAT) + if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; - else - f1 = get_tv_number(rettv); - if (var2.v_type == VAR_FLOAT) + } else { + f1 = tv_get_number(rettv); + } + if (var2.v_type == VAR_FLOAT) { f2 = var2.vval.v_float; - else - f2 = get_tv_number(&var2); - n1 = FALSE; + } else { + f2 = tv_get_number(&var2); + } + n1 = false; switch (type) { - case TYPE_EQUAL: n1 = (f1 == f2); break; - case TYPE_NEQUAL: n1 = (f1 != f2); break; - case TYPE_GREATER: n1 = (f1 > f2); break; - case TYPE_GEQUAL: n1 = (f1 >= f2); break; - case TYPE_SMALLER: n1 = (f1 < f2); break; - case TYPE_SEQUAL: n1 = (f1 <= f2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ + case TYPE_EQUAL: n1 = (f1 == f2); break; + case TYPE_NEQUAL: n1 = (f1 != f2); break; + case TYPE_GREATER: n1 = (f1 > f2); break; + case TYPE_GEQUAL: n1 = (f1 >= f2); break; + case TYPE_SMALLER: n1 = (f1 < f2); break; + case TYPE_SEQUAL: n1 = (f1 <= f2); break; + case TYPE_UNKNOWN: + case TYPE_MATCH: + case TYPE_NOMATCH: break; } } /* @@ -3829,48 +3712,51 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) */ else if ((rettv->v_type == VAR_NUMBER || var2.v_type == VAR_NUMBER) && type != TYPE_MATCH && type != TYPE_NOMATCH) { - n1 = get_tv_number(rettv); - n2 = get_tv_number(&var2); + n1 = tv_get_number(rettv); + n2 = tv_get_number(&var2); switch (type) { - case TYPE_EQUAL: n1 = (n1 == n2); break; - case TYPE_NEQUAL: n1 = (n1 != n2); break; - case TYPE_GREATER: n1 = (n1 > n2); break; - case TYPE_GEQUAL: n1 = (n1 >= n2); break; - case TYPE_SMALLER: n1 = (n1 < n2); break; - case TYPE_SEQUAL: n1 = (n1 <= n2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ + case TYPE_EQUAL: n1 = (n1 == n2); break; + case TYPE_NEQUAL: n1 = (n1 != n2); break; + case TYPE_GREATER: n1 = (n1 > n2); break; + case TYPE_GEQUAL: n1 = (n1 >= n2); break; + case TYPE_SMALLER: n1 = (n1 < n2); break; + case TYPE_SEQUAL: n1 = (n1 <= n2); break; + case TYPE_UNKNOWN: + case TYPE_MATCH: + case TYPE_NOMATCH: break; } } else { - s1 = get_tv_string_buf(rettv, buf1); - s2 = get_tv_string_buf(&var2, buf2); - if (type != TYPE_MATCH && type != TYPE_NOMATCH) - i = ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2); - else + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *const s1 = tv_get_string_buf(rettv, buf1); + const char *const s2 = tv_get_string_buf(&var2, buf2); + if (type != TYPE_MATCH && type != TYPE_NOMATCH) { + i = mb_strcmp_ic((bool)ic, s1, s2); + } else { i = 0; - n1 = FALSE; + } + n1 = false; switch (type) { - case TYPE_EQUAL: n1 = (i == 0); break; - case TYPE_NEQUAL: n1 = (i != 0); break; - case TYPE_GREATER: n1 = (i > 0); break; - case TYPE_GEQUAL: n1 = (i >= 0); break; - case TYPE_SMALLER: n1 = (i < 0); break; - case TYPE_SEQUAL: n1 = (i <= 0); break; - - case TYPE_MATCH: - case TYPE_NOMATCH: - n1 = pattern_match(s2, s1, ic); - if (type == TYPE_NOMATCH) { - n1 = !n1; + case TYPE_EQUAL: n1 = (i == 0); break; + case TYPE_NEQUAL: n1 = (i != 0); break; + case TYPE_GREATER: n1 = (i > 0); break; + case TYPE_GEQUAL: n1 = (i >= 0); break; + case TYPE_SMALLER: n1 = (i < 0); break; + case TYPE_SEQUAL: n1 = (i <= 0); break; + + case TYPE_MATCH: + case TYPE_NOMATCH: { + n1 = pattern_match((char_u *)s2, (char_u *)s1, ic); + if (type == TYPE_NOMATCH) { + n1 = !n1; + } + break; } - break; - - case TYPE_UNKNOWN: break; /* avoid gcc warning */ + case TYPE_UNKNOWN: break; // Avoid gcc warning. } } - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = n1; } @@ -3879,6 +3765,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle fourth level expression: * + number addition @@ -3897,8 +3785,6 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) int op; long n1, n2; float_T f1 = 0, f2 = 0; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; char_u *p; /* @@ -3916,17 +3802,16 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) break; if ((op != '+' || rettv->v_type != VAR_LIST) - && (op == '.' || rettv->v_type != VAR_FLOAT) - ) { - /* For "list + ...", an illegal use of the first operand as - * a number cannot be determined before evaluating the 2nd - * operand: if this is also a list, all is ok. - * For "something . ...", "something - ..." or "non-list + ...", - * we know that the first operand needs to be a string or number - * without evaluating the 2nd operand. So check before to avoid - * side effects after an error. */ - if (evaluate && get_tv_string_chk(rettv) == NULL) { - clear_tv(rettv); + && (op == '.' || rettv->v_type != VAR_FLOAT)) { + // For "list + ...", an illegal use of the first operand as + // a number cannot be determined before evaluating the 2nd + // operand: if this is also a list, all is ok. + // For "something . ...", "something - ..." or "non-list + ...", + // we know that the first operand needs to be a string or number + // without evaluating the 2nd operand. So check before to avoid + // side effects after an error. + if (evaluate && !tv_check_str(rettv)) { + tv_clear(rettv); return FAIL; } } @@ -3936,7 +3821,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) */ *arg = skipwhite(*arg + 1); if (eval6(arg, &var2, evaluate, op == '.') == FAIL) { - clear_tv(rettv); + tv_clear(rettv); return FAIL; } @@ -3945,41 +3830,44 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (op == '.') { - s1 = get_tv_string_buf(rettv, buf1); /* already checked */ - s2 = get_tv_string_buf_chk(&var2, buf2); - if (s2 == NULL) { /* type error ? */ - clear_tv(rettv); - clear_tv(&var2); + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + // s1 already checked + const char *const s1 = tv_get_string_buf(rettv, buf1); + const char *const s2 = tv_get_string_buf_chk(&var2, buf2); + if (s2 == NULL) { // Type error? + tv_clear(rettv); + tv_clear(&var2); return FAIL; } - p = concat_str(s1, s2); - clear_tv(rettv); + p = concat_str((const char_u *)s1, (const char_u *)s2); + tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; } else if (op == '+' && rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST) { - /* concatenate Lists */ - if (list_concat(rettv->vval.v_list, var2.vval.v_list, - &var3) == FAIL) { - clear_tv(rettv); - clear_tv(&var2); + // Concatenate Lists. + if (tv_list_concat(rettv->vval.v_list, var2.vval.v_list, &var3) + == FAIL) { + tv_clear(rettv); + tv_clear(&var2); return FAIL; } - clear_tv(rettv); + tv_clear(rettv); *rettv = var3; } else { - int error = FALSE; + bool error = false; if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; n1 = 0; } else { - n1 = get_tv_number_chk(rettv, &error); + n1 = tv_get_number_chk(rettv, &error); if (error) { /* This can only happen for "list + non-list". For * "non-list + ..." or "something - ...", we returned * before evaluating the 2nd operand. */ - clear_tv(rettv); + tv_clear(rettv); return FAIL; } if (var2.v_type == VAR_FLOAT) @@ -3989,16 +3877,16 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) f2 = var2.vval.v_float; n2 = 0; } else { - n2 = get_tv_number_chk(&var2, &error); + n2 = tv_get_number_chk(&var2, &error); if (error) { - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } if (rettv->v_type == VAR_FLOAT) f2 = n2; } - clear_tv(rettv); + tv_clear(rettv); /* If there is a float on either side the result is a float. */ if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) { @@ -4017,12 +3905,14 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) rettv->vval.v_number = n1; } } - clear_tv(&var2); + tv_clear(&var2); } } return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle fifth level expression: * * number multiplication @@ -4047,7 +3937,7 @@ eval6 ( long n1, n2; int use_float = FALSE; float_T f1 = 0, f2; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -4068,13 +3958,16 @@ eval6 ( f1 = rettv->vval.v_float; use_float = TRUE; n1 = 0; - } else - n1 = get_tv_number_chk(rettv, &error); - clear_tv(rettv); - if (error) + } else { + n1 = tv_get_number_chk(rettv, &error); + } + tv_clear(rettv); + if (error) { return FAIL; - } else + } + } else { n1 = 0; + } /* * Get the second variable. @@ -4092,12 +3985,14 @@ eval6 ( f2 = var2.vval.v_float; n2 = 0; } else { - n2 = get_tv_number_chk(&var2, &error); - clear_tv(&var2); - if (error) + n2 = tv_get_number_chk(&var2, &error); + tv_clear(&var2); + if (error) { return FAIL; - if (use_float) + } + if (use_float) { f2 = n2; + } } /* @@ -4155,6 +4050,8 @@ eval6 ( return OK; } +// TODO(ZyX-I): move to eval/expressions + // Handle sixth level expression: // number number constant // "string" string constant @@ -4193,11 +4090,11 @@ static int eval7( int ret = OK; char_u *alias; - // Initialise variable so that clear_tv() can't mistake this for a + // Initialise variable so that tv_clear() can't mistake this for a // string and free a string that isn't there. rettv->v_type = VAR_UNKNOWN; - // Skip '!' and '-' characters. They are handled later. + // Skip '!', '-' and '+' characters. They are handled later. start_leader = *arg; while (**arg == '!' || **arg == '-' || **arg == '+') { *arg = skipwhite(*arg + 1); @@ -4309,7 +4206,7 @@ static int eval7( ++*arg; } else if (ret == OK) { EMSG(_("E110: Missing ')'")); - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } break; @@ -4350,7 +4247,7 @@ static int eval7( // get_func_tv, but it's needed in handle_subscript() to parse // what follows. So set it here. if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { - rettv->vval.v_string = empty_string; + rettv->vval.v_string = (char_u *)tv_empty_string; rettv->v_type = VAR_FUNC; } @@ -4359,7 +4256,7 @@ static int eval7( // an exception was thrown but not caught. if (aborting()) { if (ret == OK) { - clear_tv(rettv); + tv_clear(rettv); } ret = FAIL; } @@ -4383,17 +4280,17 @@ static int eval7( // Apply logical NOT and unary '-', from right to left, ignore '+'. if (ret == OK && evaluate && end_leader > start_leader) { - int error = false; + bool error = false; int val = 0; float_T f = 0.0; if (rettv->v_type == VAR_FLOAT) { f = rettv->vval.v_float; } else { - val = get_tv_number_chk(rettv, &error); + val = tv_get_number_chk(rettv, &error); } if (error) { - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } else { while (end_leader > start_leader) { @@ -4413,10 +4310,10 @@ static int eval7( } } if (rettv->v_type == VAR_FLOAT) { - clear_tv(rettv); + tv_clear(rettv); rettv->vval.v_float = f; } else { - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = val; } @@ -4426,6 +4323,8 @@ static int eval7( return ret; } +// TODO(ZyX-I): move to eval/expressions + /* * Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key". * "*arg" points to the '[' or '.'. @@ -4439,12 +4338,11 @@ eval_index ( int verbose /* give error messages */ ) { - int empty1 = FALSE, empty2 = FALSE; - typval_T var1, var2; + bool empty1 = false; + bool empty2 = false; long n1, n2 = 0; - long len = -1; - int range = FALSE; - char_u *s; + ptrdiff_t len = -1; + int range = false; char_u *key = NULL; switch (rettv->v_type) { @@ -4481,8 +4379,8 @@ eval_index ( } } - init_tv(&var1); - init_tv(&var2); + typval_T var1 = TV_INITIAL_VALUE; + typval_T var2 = TV_INITIAL_VALUE; if (**arg == '.') { /* * dict.name @@ -4500,13 +4398,13 @@ eval_index ( * Get the (first) variable from inside the []. */ *arg = skipwhite(*arg + 1); - if (**arg == ':') - empty1 = TRUE; - else if (eval1(arg, &var1, evaluate) == FAIL) /* recursive! */ + if (**arg == ':') { + empty1 = true; + } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive! return FAIL; - else if (evaluate && get_tv_string_chk(&var1) == NULL) { - /* not a number or string */ - clear_tv(&var1); + } else if (evaluate && !tv_check_str(&var1)) { + // Not a number or string. + tv_clear(&var1); return FAIL; } @@ -4516,28 +4414,32 @@ eval_index ( if (**arg == ':') { range = TRUE; *arg = skipwhite(*arg + 1); - if (**arg == ']') - empty2 = TRUE; - else if (eval1(arg, &var2, evaluate) == FAIL) { /* recursive! */ - if (!empty1) - clear_tv(&var1); + if (**arg == ']') { + empty2 = true; + } else if (eval1(arg, &var2, evaluate) == FAIL) { // Recursive! + if (!empty1) { + tv_clear(&var1); + } return FAIL; - } else if (evaluate && get_tv_string_chk(&var2) == NULL) { - /* not a number or string */ - if (!empty1) - clear_tv(&var1); - clear_tv(&var2); + } else if (evaluate && !tv_check_str(&var2)) { + // Not a number or string. + if (!empty1) { + tv_clear(&var1); + } + tv_clear(&var2); return FAIL; } } /* Check for the ']'. */ if (**arg != ']') { - if (verbose) - EMSG(_(e_missbrac)); - clear_tv(&var1); - if (range) - clear_tv(&var2); + if (verbose) { + emsgf(_(e_missbrac)); + } + tv_clear(&var1); + if (range) { + tv_clear(&var2); + } return FAIL; } *arg = skipwhite(*arg + 1); /* skip the ']' */ @@ -4546,139 +4448,155 @@ eval_index ( if (evaluate) { n1 = 0; if (!empty1 && rettv->v_type != VAR_DICT) { - n1 = get_tv_number(&var1); - clear_tv(&var1); + n1 = tv_get_number(&var1); + tv_clear(&var1); } if (range) { - if (empty2) + if (empty2) { n2 = -1; - else { - n2 = get_tv_number(&var2); - clear_tv(&var2); + } else { + n2 = tv_get_number(&var2); + tv_clear(&var2); } } switch (rettv->v_type) { - case VAR_NUMBER: - case VAR_STRING: - s = get_tv_string(rettv); - len = (long)STRLEN(s); - if (range) { - /* The resulting variable is a substring. If the indexes - * are out of range the result is empty. */ + case VAR_NUMBER: + case VAR_STRING: { + const char *const s = tv_get_string(rettv); + char *v; + len = (ptrdiff_t)strlen(s); + if (range) { + // The resulting variable is a substring. If the indexes + // are out of range the result is empty. + if (n1 < 0) { + n1 = len + n1; + if (n1 < 0) { + n1 = 0; + } + } + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len; + } + if (n1 >= len || n2 < 0 || n1 > n2) { + v = NULL; + } else { + v = xmemdupz(s + n1, (size_t)(n2 - n1 + 1)); + } + } else { + // The resulting variable is a string of a single + // character. If the index is too big or negative the + // result is empty. + if (n1 >= len || n1 < 0) { + v = NULL; + } else { + v = xmemdupz(s + n1, 1); + } + } + tv_clear(rettv); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (char_u *)v; + break; + } + case VAR_LIST: { + len = tv_list_len(rettv->vval.v_list); if (n1 < 0) { n1 = len + n1; - if (n1 < 0) - n1 = 0; } - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len; - if (n1 >= len || n2 < 0 || n1 > n2) - s = NULL; - else - s = vim_strnsave(s + n1, (int)(n2 - n1 + 1)); - } else { - /* The resulting variable is a string of a single - * character. If the index is too big or negative the - * result is empty. */ - if (n1 >= len || n1 < 0) - s = NULL; - else - s = vim_strnsave(s + n1, 1); - } - clear_tv(rettv); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = s; - break; - - case VAR_LIST: - len = list_len(rettv->vval.v_list); - if (n1 < 0) - n1 = len + n1; - if (!empty1 && (n1 < 0 || n1 >= len)) { - /* For a range we allow invalid values and return an empty - * list. A list index out of range is an error. */ - if (!range) { - if (verbose) - EMSGN(_(e_listidx), n1); - return FAIL; + if (!empty1 && (n1 < 0 || n1 >= len)) { + // For a range we allow invalid values and return an empty + // list. A list index out of range is an error. + if (!range) { + if (verbose) { + EMSGN(_(e_listidx), n1); + } + return FAIL; + } + n1 = len; } - n1 = len; - } - if (range) { - list_T *l; - listitem_T *item; - - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len - 1; - if (!empty2 && (n2 < 0 || n2 + 1 < n1)) - n2 = -1; - l = list_alloc(); - item = list_find(rettv->vval.v_list, n1); - while (n1++ <= n2) { - list_append_tv(l, &item->li_tv); - item = item->li_next; + if (range) { + list_T *l; + listitem_T *item; + + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len - 1; + } + if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { + n2 = -1; + } + l = tv_list_alloc(); + item = tv_list_find(rettv->vval.v_list, n1); + while (n1++ <= n2) { + tv_list_append_tv(l, &item->li_tv); + item = item->li_next; + } + tv_clear(rettv); + rettv->v_type = VAR_LIST; + rettv->vval.v_list = l; + l->lv_refcount++; + } else { + tv_copy(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_clear(rettv); + *rettv = var1; } - clear_tv(rettv); - rettv->v_type = VAR_LIST; - rettv->vval.v_list = l; - ++l->lv_refcount; - } else { - copy_tv(&list_find(rettv->vval.v_list, n1)->li_tv, &var1); - clear_tv(rettv); - *rettv = var1; - } - break; - - case VAR_DICT: - if (range) { - if (verbose) - EMSG(_(e_dictrange)); - if (len == -1) - clear_tv(&var1); - return FAIL; + break; } - { - dictitem_T *item; + case VAR_DICT: { + if (range) { + if (verbose) { + emsgf(_(e_dictrange)); + } + if (len == -1) { + tv_clear(&var1); + } + return FAIL; + } if (len == -1) { - key = get_tv_string_chk(&var1); + key = (char_u *)tv_get_string_chk(&var1); if (key == NULL) { - clear_tv(&var1); + tv_clear(&var1); return FAIL; } } - item = dict_find(rettv->vval.v_dict, key, (int)len); + dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, + (const char *)key, len); - if (item == NULL && verbose) - EMSG2(_(e_dictkey), key); - if (len == -1) - clear_tv(&var1); - if (item == NULL) + if (item == NULL && verbose) { + emsgf(_(e_dictkey), key); + } + if (len == -1) { + tv_clear(&var1); + } + if (item == NULL) { return FAIL; + } - copy_tv(&item->di_tv, &var1); - clear_tv(rettv); + tv_copy(&item->di_tv, &var1); + tv_clear(rettv); *rettv = var1; + break; + } + case VAR_SPECIAL: + case VAR_FUNC: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_UNKNOWN: { + break; // Not evaluating, skipping over subscript } - break; - case VAR_SPECIAL: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; // Not evaluating, skipping over subscript } } return OK; } +// TODO(ZyX-I): move to eval/executor + /// Get an option value /// /// @param[in,out] arg Points to the '&' or '+' before the option name. Is @@ -4861,7 +4779,10 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) } *name = NUL; - *arg = p + 1; + if (*p != NUL) { // just in case + p++; + } + *arg = p; return OK; } @@ -4929,13 +4850,15 @@ char_u *partial_name(partial_T *pt) return pt->pt_func->uf_name; } +// TODO(ZyX-I): Move to eval/typval.h + static void partial_free(partial_T *pt) { for (int i = 0; i < pt->pt_argc; i++) { - clear_tv(&pt->pt_argv[i]); + tv_clear(&pt->pt_argv[i]); } xfree(pt->pt_argv); - dict_unref(pt->pt_dict); + tv_dict_unref(pt->pt_dict); if (pt->pt_name != NULL) { func_unref(pt->pt_name); xfree(pt->pt_name); @@ -4945,6 +4868,8 @@ static void partial_free(partial_T *pt) xfree(pt); } +// TODO(ZyX-I): Move to eval/typval.h + /// Unreference a closure: decrement the reference count and free it when it /// becomes zero. void partial_unref(partial_T *pt) @@ -4963,7 +4888,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) listitem_T *item; if (evaluate) { - l = list_alloc(); + l = tv_list_alloc(); } *arg = skipwhite(*arg + 1); @@ -4971,10 +4896,10 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ goto failret; if (evaluate) { - item = listitem_alloc(); + item = tv_list_item_alloc(); item->li_tv = tv; item->li_tv.v_lock = 0; - list_append(l, item); + tv_list_append(l, item); } if (**arg == ']') @@ -4990,7 +4915,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) EMSG2(_("E697: Missing end of List ']': %s"), *arg); failret: if (evaluate) { - list_free(l); + tv_list_free(l); } return FAIL; } @@ -5005,196 +4930,7 @@ failret: return OK; } -/* - * Allocate an empty header for a list. - * Caller should take care of the reference count. - */ -list_T *list_alloc(void) FUNC_ATTR_NONNULL_RET -{ - list_T *list = xcalloc(1, sizeof(list_T)); - - /* Prepend the list to the list of lists for garbage collection. */ - if (first_list != NULL) - first_list->lv_used_prev = list; - list->lv_used_prev = NULL; - list->lv_used_next = first_list; - first_list = list; - return list; -} - -/* - * Allocate an empty list for a return value. - */ -static list_T *rettv_list_alloc(typval_T *rettv) -{ - list_T *l = list_alloc(); - rettv->vval.v_list = l; - rettv->v_type = VAR_LIST; - rettv->v_lock = VAR_UNLOCKED; - l->lv_refcount++; - return l; -} - -/// Unreference a list: decrement the reference count and free it when it -/// becomes zero. -void list_unref(list_T *l) { - if (l != NULL && --l->lv_refcount <= 0) { - list_free(l); - } -} - -/// Free a list, including all items it points to. -/// Ignores the reference count. -static void list_free_contents(list_T *l) { - listitem_T *item; - - for (item = l->lv_first; item != NULL; item = l->lv_first) { - // Remove the item before deleting it. - l->lv_first = item->li_next; - clear_tv(&item->li_tv); - xfree(item); - } -} - -static void list_free_list(list_T *l) { - // Remove the list from the list of lists for garbage collection. - if (l->lv_used_prev == NULL) { - first_list = l->lv_used_next; - } else { - l->lv_used_prev->lv_used_next = l->lv_used_next; - } - if (l->lv_used_next != NULL) { - l->lv_used_next->lv_used_prev = l->lv_used_prev; - } - - xfree(l); -} - -void list_free(list_T *l) { - if (!in_free_unref_items) { - list_free_contents(l); - list_free_list(l); - } -} - -/* - * Allocate a list item. - * It is not initialized, don't forget to set v_lock. - */ -listitem_T *listitem_alloc(void) FUNC_ATTR_NONNULL_RET -{ - return xmalloc(sizeof(listitem_T)); -} - -/* - * Free a list item. Also clears the value. Does not notify watchers. - */ -void listitem_free(listitem_T *item) -{ - clear_tv(&item->li_tv); - xfree(item); -} - -/* - * Remove a list item from a List and free it. Also clears the value. - */ -void listitem_remove(list_T *l, listitem_T *item) -{ - vim_list_remove(l, item, item); - listitem_free(item); -} - -/* - * Get the number of items in a list. - */ -static long list_len(list_T *l) -{ - if (l == NULL) - return 0L; - return l->lv_len; -} - -/* - * Return TRUE when two lists have exactly the same values. - */ -static int -list_equal ( - list_T *l1, - list_T *l2, - int ic, /* ignore case for strings */ - int recursive /* TRUE when used recursively */ -) -{ - listitem_T *item1, *item2; - - if (l1 == NULL || l2 == NULL) - return FALSE; - if (l1 == l2) - return TRUE; - if (list_len(l1) != list_len(l2)) - return FALSE; - - for (item1 = l1->lv_first, item2 = l2->lv_first; - item1 != NULL && item2 != NULL; - item1 = item1->li_next, item2 = item2->li_next) - if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) - return FALSE; - return item1 == NULL && item2 == NULL; -} - -/* - * Return the dictitem that an entry in a hashtable points to. - */ -dictitem_T *dict_lookup(hashitem_T *hi) -{ - return HI2DI(hi); -} - -/* - * Return TRUE when two dictionaries have exactly the same key/values. - */ -static int -dict_equal ( - dict_T *d1, - dict_T *d2, - int ic, /* ignore case for strings */ - int recursive /* TRUE when used recursively */ -) -{ - hashitem_T *hi; - dictitem_T *item2; - int todo; - - if (d1 == NULL && d2 == NULL) { - return true; - } - if (d1 == NULL || d2 == NULL) { - return false; - } - if (d1 == d2) { - return true; - } - if (dict_len(d1) != dict_len(d2)) { - return false; - } - - todo = (int)d1->dv_hashtab.ht_used; - for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - item2 = dict_find(d2, hi->hi_key, -1); - if (item2 == NULL) - return FALSE; - if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive)) - return FALSE; - --todo; - } - } - return TRUE; -} - -static int tv_equal_recurse_limit; - -static bool func_equal( +bool func_equal( typval_T *tv1, typval_T *tv2, bool ic // ignore case @@ -5229,7 +4965,7 @@ static bool func_equal( if (d1 != d2) { return false; } - } else if (!dict_equal(d1, d2, ic, true)) { + } else if (!tv_dict_equal(d1, d2, ic, true)) { return false; } @@ -5248,551 +4984,6 @@ static bool func_equal( return true; } -/* - * Return TRUE if "tv1" and "tv2" have the same value. - * Compares the items just like "==" would compare them, but strings and - * numbers are different. Floats and numbers are also different. - */ -static int -tv_equal ( - typval_T *tv1, - typval_T *tv2, - int ic, /* ignore case */ - int recursive /* TRUE when used recursively */ -) -{ - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; - char_u *s1, *s2; - static int recursive_cnt = 0; /* catch recursive loops */ - int r; - - /* Catch lists and dicts that have an endless loop by limiting - * recursiveness to a limit. We guess they are equal then. - * A fixed limit has the problem of still taking an awful long time. - * Reduce the limit every time running into it. That should work fine for - * deeply linked structures that are not recursively linked and catch - * recursiveness quickly. */ - if (!recursive) - tv_equal_recurse_limit = 1000; - if (recursive_cnt >= tv_equal_recurse_limit) { - --tv_equal_recurse_limit; - return TRUE; - } - - // For VAR_FUNC and VAR_PARTIAL compare the function name, bound dict and - // arguments. - if ((tv1->v_type == VAR_FUNC - || (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL)) - && (tv2->v_type == VAR_FUNC - || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL))) { - recursive_cnt++; - r = func_equal(tv1, tv2, ic); - recursive_cnt--; - return r; - } - if (tv1->v_type != tv2->v_type) { - return false; - } - - switch (tv1->v_type) { - case VAR_LIST: - ++recursive_cnt; - r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE); - --recursive_cnt; - return r; - - case VAR_DICT: - ++recursive_cnt; - r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE); - --recursive_cnt; - return r; - - case VAR_NUMBER: - return tv1->vval.v_number == tv2->vval.v_number; - - case VAR_FLOAT: - return tv1->vval.v_float == tv2->vval.v_float; - - case VAR_STRING: - s1 = get_tv_string_buf(tv1, buf1); - s2 = get_tv_string_buf(tv2, buf2); - return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; - - case VAR_SPECIAL: - return tv1->vval.v_special == tv2->vval.v_special; - - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_UNKNOWN: - // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does - // not equal anything, not even self. - return false; - } - - assert(false); - return false; -} - -/* - * Locate item with index "n" in list "l" and return it. - * A negative index is counted from the end; -1 is the last item. - * Returns NULL when "n" is out of range. - */ -listitem_T *list_find(list_T *l, long n) -{ - listitem_T *item; - long idx; - - if (l == NULL) - return NULL; - - /* Negative index is relative to the end. */ - if (n < 0) - n = l->lv_len + n; - - /* Check for index out of range. */ - if (n < 0 || n >= l->lv_len) - return NULL; - - /* When there is a cached index may start search from there. */ - if (l->lv_idx_item != NULL) { - if (n < l->lv_idx / 2) { - /* closest to the start of the list */ - item = l->lv_first; - idx = 0; - } else if (n > (l->lv_idx + l->lv_len) / 2) { - /* closest to the end of the list */ - item = l->lv_last; - idx = l->lv_len - 1; - } else { - /* closest to the cached index */ - item = l->lv_idx_item; - idx = l->lv_idx; - } - } else { - if (n < l->lv_len / 2) { - /* closest to the start of the list */ - item = l->lv_first; - idx = 0; - } else { - /* closest to the end of the list */ - item = l->lv_last; - idx = l->lv_len - 1; - } - } - - while (n > idx) { - /* search forward */ - item = item->li_next; - ++idx; - } - while (n < idx) { - /* search backward */ - item = item->li_prev; - --idx; - } - - /* cache the used index */ - l->lv_idx = idx; - l->lv_idx_item = item; - - return item; -} - -/* - * Get list item "l[idx]" as a number. - */ -static long -list_find_nr ( - list_T *l, - long idx, - int *errorp /* set to TRUE when something wrong */ -) -{ - listitem_T *li; - - li = list_find(l, idx); - if (li == NULL) { - if (errorp != NULL) - *errorp = TRUE; - return -1L; - } - return get_tv_number_chk(&li->li_tv, errorp); -} - -/* - * Get list item "l[idx - 1]" as a string. Returns NULL for failure. - */ -char_u *list_find_str(list_T *l, long idx) -{ - listitem_T *li; - - li = list_find(l, idx - 1); - if (li == NULL) { - EMSGN(_(e_listidx), idx); - return NULL; - } - return get_tv_string(&li->li_tv); -} - -/* - * Locate "item" list "l" and return its index. - * Returns -1 when "item" is not in the list. - */ -static long list_idx_of_item(list_T *l, listitem_T *item) -{ - long idx = 0; - listitem_T *li; - - if (l == NULL) - return -1; - idx = 0; - for (li = l->lv_first; li != NULL && li != item; li = li->li_next) - ++idx; - if (li == NULL) - return -1; - return idx; -} - -/* - * Append item "item" to the end of list "l". - */ -void list_append(list_T *l, listitem_T *item) -{ - if (l->lv_last == NULL) { - /* empty list */ - l->lv_first = item; - l->lv_last = item; - item->li_prev = NULL; - } else { - l->lv_last->li_next = item; - item->li_prev = l->lv_last; - l->lv_last = item; - } - ++l->lv_len; - item->li_next = NULL; -} - -/* - * Append typval_T "tv" to the end of list "l". - */ -void list_append_tv(list_T *l, typval_T *tv) -{ - listitem_T *li = listitem_alloc(); - copy_tv(tv, &li->li_tv); - list_append(l, li); -} - -/* - * Add a list to a list. - */ -void list_append_list(list_T *list, list_T *itemlist) -{ - listitem_T *li = listitem_alloc(); - - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = itemlist; - list_append(list, li); - ++itemlist->lv_refcount; -} - -/* - * Add a dictionary to a list. Used by getqflist(). - */ -void list_append_dict(list_T *list, dict_T *dict) -{ - listitem_T *li = listitem_alloc(); - - li->li_tv.v_type = VAR_DICT; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_dict = dict; - list_append(list, li); - ++dict->dv_refcount; -} - -/// Make a copy of "str" and append it as an item to list "l" -/// -/// @param[out] l List to append to. -/// @param[in] str String to append. -/// @param[in] len Length of the appended string. May be negative, in this -/// case string is considered to be usual zero-terminated -/// string. -void list_append_string(list_T *l, const char_u *str, int len) - FUNC_ATTR_NONNULL_ARG(1) -{ - if (str == NULL) { - list_append_allocated_string(l, NULL); - } else { - list_append_allocated_string(l, (len >= 0 - ? xmemdupz((char *) str, len) - : xstrdup((char *) str))); - } -} - -/// Append given string to the list -/// -/// Unlike list_append_string this function does not copy the string. -/// -/// @param[out] l List to append to. -/// @param[in] str String to append. -void list_append_allocated_string(list_T *l, char *const str) - FUNC_ATTR_NONNULL_ARG(1) -{ - listitem_T *li = listitem_alloc(); - - list_append(l, li); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *) str; -} - -/* - * Append "n" to list "l". - */ -void list_append_number(list_T *l, varnumber_T n) -{ - listitem_T *li = listitem_alloc(); - li->li_tv.v_type = VAR_NUMBER; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_number = n; - list_append(l, li); -} - -/* - * Insert typval_T "tv" in list "l" before "item". - * If "item" is NULL append at the end. - */ -void list_insert_tv(list_T *l, typval_T *tv, listitem_T *item) -{ - listitem_T *ni = listitem_alloc(); - - copy_tv(tv, &ni->li_tv); - list_insert(l, ni, item); -} - -void list_insert(list_T *l, listitem_T *ni, listitem_T *item) -{ - if (item == NULL) - /* Append new item at end of list. */ - list_append(l, ni); - else { - /* Insert new item before existing item. */ - ni->li_prev = item->li_prev; - ni->li_next = item; - if (item->li_prev == NULL) { - l->lv_first = ni; - ++l->lv_idx; - } else { - item->li_prev->li_next = ni; - l->lv_idx_item = NULL; - } - item->li_prev = ni; - ++l->lv_len; - } -} - -/* - * Extend "l1" with "l2". - * If "bef" is NULL append at the end, otherwise insert before this item. - */ -static void list_extend(list_T *l1, list_T *l2, listitem_T *bef) -{ - listitem_T *item; - int todo = l2->lv_len; - - /* We also quit the loop when we have inserted the original item count of - * the list, avoid a hang when we extend a list with itself. */ - for (item = l2->lv_first; item != NULL && --todo >= 0; item = item->li_next) { - list_insert_tv(l1, &item->li_tv, bef); - } -} - -/* - * Concatenate lists "l1" and "l2" into a new list, stored in "tv". - * Return FAIL on failure to copy. - */ -static int list_concat(list_T *l1, list_T *l2, typval_T *tv) -{ - list_T *l; - - if (l1 == NULL || l2 == NULL) - return FAIL; - - /* make a copy of the first list. */ - l = list_copy(NULL, l1, false, 0); - if (l == NULL) - return FAIL; - tv->v_type = VAR_LIST; - tv->vval.v_list = l; - - /* append all items from the second list */ - list_extend(l, l2, NULL); - return OK; -} - -/// Make a copy of list -/// -/// @param[in] conv If non-NULL, then all internal strings will be converted. -/// @param[in] orig Original list to copy. -/// @param[in] deep If false, then shallow copy will be done. -/// @param[in] copyID See var_item_copy(). -/// -/// @return Copied list. May be NULL in case original list is NULL or some -/// failure happens. The refcount of the new list is set to 1. -static list_T *list_copy(const vimconv_T *const conv, - list_T *const orig, - const bool deep, - const int copyID) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - listitem_T *item; - listitem_T *ni; - - if (orig == NULL) - return NULL; - - list_T *copy = list_alloc(); - if (copyID != 0) { - /* Do this before adding the items, because one of the items may - * refer back to this list. */ - orig->lv_copyID = copyID; - orig->lv_copylist = copy; - } - for (item = orig->lv_first; item != NULL && !got_int; - item = item->li_next) { - ni = listitem_alloc(); - if (deep) { - if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { - xfree(ni); - break; - } - } else - copy_tv(&item->li_tv, &ni->li_tv); - list_append(copy, ni); - } - ++copy->lv_refcount; - if (item != NULL) { - list_unref(copy); - copy = NULL; - } - - return copy; -} - -/// Remove items "item" to "item2" from list "l". -/// @warning Does not free the listitem or the value! -void vim_list_remove(list_T *l, listitem_T *item, listitem_T *item2) -{ - // notify watchers - for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { - --l->lv_len; - list_fix_watch(l, ip); - if (ip == item2) { - break; - } - } - - if (item2->li_next == NULL) { - l->lv_last = item->li_prev; - } else { - item2->li_next->li_prev = item->li_prev; - } - if (item->li_prev == NULL) { - l->lv_first = item2->li_next; - } else { - item->li_prev->li_next = item2->li_next; - } - l->lv_idx_item = NULL; -} - -typedef struct join_S { - char_u *s; - char_u *tofree; -} join_T; - -/// Join list into a string, helper function -/// -/// @param[out] gap Garray where result will be saved. -/// @param[in] l List to join. -/// @param[in] sep Used separator. -/// @param[in] join_gap Garray to keep each list item string. -/// -/// @return OK in case of success, FAIL otherwise. -static int list_join_inner(garray_T *const gap, list_T *const l, - const char *const sep, garray_T *const join_gap) - FUNC_ATTR_NONNULL_ALL -{ - int sumlen = 0; - bool first = true; - listitem_T *item; - - /* Stringify each item in the list. */ - for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { - char *s; - size_t len; - s = encode_tv2echo(&item->li_tv, &len); - if (s == NULL) { - return FAIL; - } - - sumlen += (int) len; - - join_T *const p = GA_APPEND_VIA_PTR(join_T, join_gap); - p->tofree = p->s = (char_u *) s; - - line_breakcheck(); - } - - /* Allocate result buffer with its total size, avoid re-allocation and - * multiple copy operations. Add 2 for a tailing ']' and NUL. */ - if (join_gap->ga_len >= 2) - sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1); - ga_grow(gap, sumlen + 2); - - for (int i = 0; i < join_gap->ga_len && !got_int; ++i) { - if (first) { - first = false; - } else { - ga_concat(gap, (const char_u *) sep); - } - const join_T *const p = ((const join_T *)join_gap->ga_data) + i; - - if (p->s != NULL) - ga_concat(gap, p->s); - line_breakcheck(); - } - - return OK; -} - -/// Join list into a string using given separator -/// -/// @param[out] gap Garray where result will be saved. -/// @param[in] l Joined list. -/// @param[in] sep Separator. -/// -/// @return OK in case of success, FAIL otherwise. -static int list_join(garray_T *const gap, list_T *const l, - const char *const sep) - FUNC_ATTR_NONNULL_ALL -{ - if (l->lv_len < 1) { - return OK; - } - - garray_T join_ga; - int retval; - - ga_init(&join_ga, (int)sizeof(join_T), l->lv_len); - retval = list_join_inner(gap, l, sep, &join_ga); - -# define FREE_JOIN_TOFREE(join) xfree((join)->tofree) - GA_DEEP_CLEAR(&join_ga, join_T, FREE_JOIN_TOFREE); - - return retval; -} - /// Get next (unique) copy ID /// /// Used for traversing nested structures e.g. when serializing them or garbage @@ -6033,7 +5224,7 @@ bool garbage_collect(bool testing) /// Free lists and dictionaries that are no longer referenced. /// -/// Note: This function may only be called from garbage_collect(). +/// @note This function may only be called from garbage_collect(). /// /// @param copyID Free lists/dictionaries that don't have this ID. /// @return true, if something was freed. @@ -6046,19 +5237,19 @@ static int free_unref_items(int copyID) // Let all "free" functions know that we are here. This means no // dictionaries, lists, or jobs are to be freed, because we will // do that here. - in_free_unref_items = true; + tv_in_free_unref_items = true; // PASS 1: free the contents of the items. We don't free the items // themselves yet, so that it is possible to decrement refcount counters. // Go through the list of dicts and free items without the copyID. // Don't free dicts that are referenced internally. - for (dict_T *dd = first_dict; dd != NULL; dd = dd->dv_used_next) { + for (dict_T *dd = gc_first_dict; dd != NULL; dd = dd->dv_used_next) { if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { // Free the Dictionary and ordinary items it contains, but don't // recurse into Lists and Dictionaries, they will be in the list // of dicts or list of lists. - dict_free_contents(dd); + tv_dict_free_contents(dd); did_free = true; } } @@ -6066,36 +5257,36 @@ static int free_unref_items(int copyID) // Go through the list of lists and free items without the copyID. // But don't free a list that has a watcher (used in a for loop), these // are not referenced anywhere. - for (list_T *ll = first_list; ll != NULL; ll = ll->lv_used_next) { + for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) { if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) && ll->lv_watch == NULL) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. - list_free_contents(ll); + tv_list_free_contents(ll); did_free = true; } } // PASS 2: free the items themselves. - for (dd = first_dict; dd != NULL; dd = dd_next) { + for (dd = gc_first_dict; dd != NULL; dd = dd_next) { dd_next = dd->dv_used_next; if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { - dict_free_dict(dd); + tv_dict_free_dict(dd); } } - for (ll = first_list; ll != NULL; ll = ll_next) { + for (ll = gc_first_list; ll != NULL; ll = ll_next) { ll_next = ll->lv_used_next; if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) && ll->lv_watch == NULL) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. - list_free_list(ll); + tv_list_free_list(ll); } } - in_free_unref_items = false; + tv_in_free_unref_items = false; return did_free; } @@ -6118,14 +5309,10 @@ bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack) // Mark each item in the hashtab. If the item contains a hashtab // it is added to ht_stack, if it contains a list it is added to // list_stack. - int todo = (int)cur_ht->ht_used; - for (hashitem_T *hi = cur_ht->ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID, &ht_stack, - list_stack); - } - } + HASHTAB_ITER(cur_ht, hi, { + abort = abort || set_ref_in_item( + &TV_DICT_HI2DI(hi)->di_tv, copyID, &ht_stack, list_stack); + }); } if (ht_stack == NULL) { @@ -6217,7 +5404,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, QUEUE *w = NULL; DictWatcher *watcher = NULL; QUEUE_FOREACH(w, &dd->watchers) { - watcher = dictwatcher_node_data(w); + watcher = tv_dict_watcher_node_data(w); set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack); } } @@ -6362,447 +5549,6 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID) return abort; } -/// Allocate an empty header for a dictionary. -dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET -{ - dict_T *d = xmalloc(sizeof(dict_T)); - - /* Add the dict to the list of dicts for garbage collection. */ - if (first_dict != NULL) - first_dict->dv_used_prev = d; - d->dv_used_next = first_dict; - d->dv_used_prev = NULL; - first_dict = d; - - hash_init(&d->dv_hashtab); - d->dv_lock = VAR_UNLOCKED; - d->dv_scope = 0; - d->dv_refcount = 0; - d->dv_copyID = 0; - QUEUE_INIT(&d->watchers); - - return d; -} - -/* - * Allocate an empty dict for a return value. - */ -static void rettv_dict_alloc(typval_T *rettv) -{ - dict_T *d = dict_alloc(); - - rettv->vval.v_dict = d; - rettv->v_type = VAR_DICT; - rettv->v_lock = VAR_UNLOCKED; - d->dv_refcount++; -} - -/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. -/// -/// @param d The Dictionary to clear -void dict_clear(dict_T *d) - FUNC_ATTR_NONNULL_ALL -{ - hash_lock(&d->dv_hashtab); - assert(d->dv_hashtab.ht_locked > 0); - - size_t todo = d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - dictitem_free(HI2DI(hi)); - hash_remove(&d->dv_hashtab, hi); - todo--; - } - } - - hash_unlock(&d->dv_hashtab); -} - - -/* - * Unreference a Dictionary: decrement the reference count and free it when it - * becomes zero. - */ -void dict_unref(dict_T *d) -{ - if (d != NULL && --d->dv_refcount <= 0) { - dict_free(d); - } -} - -/// Free a Dictionary, including all items it contains. -/// Ignores the reference count. -static void dict_free_contents(dict_T *d) { - int todo; - hashitem_T *hi; - dictitem_T *di; - - - /* Lock the hashtab, we don't want it to resize while freeing items. */ - hash_lock(&d->dv_hashtab); - assert(d->dv_hashtab.ht_locked > 0); - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - /* Remove the item before deleting it, just in case there is - * something recursive causing trouble. */ - di = HI2DI(hi); - hash_remove(&d->dv_hashtab, hi); - dictitem_free(di); - todo--; - } - } - - while (!QUEUE_EMPTY(&d->watchers)) { - QUEUE *w = QUEUE_HEAD(&d->watchers); - DictWatcher *watcher = dictwatcher_node_data(w); - QUEUE_REMOVE(w); - dictwatcher_free(watcher); - } - - hash_clear(&d->dv_hashtab); -} - -static void dict_free_dict(dict_T *d) { - // Remove the dict from the list of dicts for garbage collection. - if (d->dv_used_prev == NULL) { - first_dict = d->dv_used_next; - } else { - d->dv_used_prev->dv_used_next = d->dv_used_next; - } - if (d->dv_used_next != NULL) { - d->dv_used_next->dv_used_prev = d->dv_used_prev; - } - - xfree(d); -} - -void dict_free(dict_T *d) { - if (!in_free_unref_items) { - dict_free_contents(d); - dict_free_dict(d); - } -} - -/* - * Allocate a Dictionary item. - * The "key" is copied to the new item. - * Note that the value of the item "di_tv" still needs to be initialized! - */ -dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET -{ - dictitem_T *di = xmalloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1); -#ifndef __clang_analyzer__ - STRCPY(di->di_key, key); -#endif - di->di_flags = DI_FLAGS_ALLOC; - return di; -} - -/* - * Make a copy of a Dictionary item. - */ -static dictitem_T *dictitem_copy(dictitem_T *org) FUNC_ATTR_NONNULL_RET -{ - dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(org->di_key)); - - STRCPY(di->di_key, org->di_key); - di->di_flags = DI_FLAGS_ALLOC; - copy_tv(&org->di_tv, &di->di_tv); - - return di; -} - -/* - * Remove item "item" from Dictionary "dict" and free it. - */ -static void dictitem_remove(dict_T *dict, dictitem_T *item) -{ - hashitem_T *hi; - - hi = hash_find(&dict->dv_hashtab, item->di_key); - if (HASHITEM_EMPTY(hi)) { - EMSG2(_(e_intern2), "dictitem_remove()"); - } else { - hash_remove(&dict->dv_hashtab, hi); - } - dictitem_free(item); -} - -/* - * Free a dict item. Also clears the value. - */ -void dictitem_free(dictitem_T *item) -{ - clear_tv(&item->di_tv); - if (item->di_flags & DI_FLAGS_ALLOC) { - xfree(item); - } -} - -/// Make a copy of dictionary -/// -/// @param[in] conv If non-NULL, then all internal strings will be converted. -/// @param[in] orig Original dictionary to copy. -/// @param[in] deep If false, then shallow copy will be done. -/// @param[in] copyID See var_item_copy(). -/// -/// @return Copied dictionary. May be NULL in case original dictionary is NULL -/// or some failure happens. The refcount of the new dictionary is set -/// to 1. -static dict_T *dict_copy(const vimconv_T *const conv, - dict_T *const orig, - const bool deep, - const int copyID) -{ - dictitem_T *di; - int todo; - hashitem_T *hi; - - if (orig == NULL) - return NULL; - - dict_T *copy = dict_alloc(); - { - if (copyID != 0) { - orig->dv_copyID = copyID; - orig->dv_copydict = copy; - } - todo = (int)orig->dv_hashtab.ht_used; - for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - - if (conv == NULL || conv->vc_type == CONV_NONE) { - di = dictitem_alloc(hi->hi_key); - } else { - char *const key = (char *) string_convert((vimconv_T *) conv, - hi->hi_key, NULL); - if (key == NULL) { - di = dictitem_alloc(hi->hi_key); - } else { - di = dictitem_alloc((char_u *) key); - xfree(key); - } - } - if (deep) { - if (var_item_copy(conv, &HI2DI(hi)->di_tv, &di->di_tv, deep, - copyID) == FAIL) { - xfree(di); - break; - } - } else - copy_tv(&HI2DI(hi)->di_tv, &di->di_tv); - if (dict_add(copy, di) == FAIL) { - dictitem_free(di); - break; - } - } - } - - ++copy->dv_refcount; - if (todo > 0) { - dict_unref(copy); - copy = NULL; - } - } - - return copy; -} - -/* - * Add item "item" to Dictionary "d". - * Returns FAIL when key already exists. - */ -int dict_add(dict_T *d, dictitem_T *item) -{ - return hash_add(&d->dv_hashtab, item->di_key); -} - -/* - * Add a number or string entry to dictionary "d". - * When "str" is NULL use number "nr", otherwise use "str". - * Returns FAIL when key already exists. - */ -int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) -{ - dictitem_T *item; - - item = dictitem_alloc((char_u *)key); - item->di_tv.v_lock = 0; - if (str == NULL) { - item->di_tv.v_type = VAR_NUMBER; - item->di_tv.vval.v_number = nr; - } else { - item->di_tv.v_type = VAR_STRING; - item->di_tv.vval.v_string = vim_strsave(str); - } - if (dict_add(d, item) == FAIL) { - dictitem_free(item); - return FAIL; - } - return OK; -} - -/* - * Add a list entry to dictionary "d". - * Returns FAIL when key already exists. - */ -int dict_add_list(dict_T *d, char *key, list_T *list) -{ - dictitem_T *item = dictitem_alloc((char_u *)key); - - item->di_tv.v_lock = 0; - item->di_tv.v_type = VAR_LIST; - item->di_tv.vval.v_list = list; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); - return FAIL; - } - ++list->lv_refcount; - return OK; -} - -/// Add a dict entry to dictionary "d". -/// Returns FAIL when out of memory and when key already exists. -int dict_add_dict(dict_T *d, char *key, dict_T *dict) -{ - dictitem_T *item = dictitem_alloc((char_u *)key); - - item->di_tv.v_lock = 0; - item->di_tv.v_type = VAR_DICT; - item->di_tv.vval.v_dict = dict; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); - return FAIL; - } - dict->dv_refcount++; - return OK; -} - -/// Set all existing keys in "dict" as read-only. -/// -/// This does not protect against adding new keys to the Dictionary. -/// -/// @param dict The dict whose keys should be frozen -void dict_set_keys_readonly(dict_T *dict) - FUNC_ATTR_NONNULL_ALL -{ - size_t todo = dict->dv_hashtab.ht_used; - for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) { - if (HASHITEM_EMPTY(hi)) { - continue; - } - todo--; - HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; - } -} - -/* - * Get the number of items in a Dictionary. - */ -static long dict_len(dict_T *d) -{ - if (d == NULL) - return 0L; - return (long)d->dv_hashtab.ht_used; -} - -/* - * Find item "key[len]" in Dictionary "d". - * If "len" is negative use strlen(key). - * Returns NULL when not found. - */ -dictitem_T *dict_find(dict_T *d, char_u *key, int len) -{ -#define AKEYLEN 200 - char_u buf[AKEYLEN]; - char_u *akey; - char_u *tofree = NULL; - hashitem_T *hi; - - if (d == NULL) { - return NULL; - } - if (len < 0) { - akey = key; - } else if (len >= AKEYLEN) { - tofree = akey = vim_strnsave(key, len); - } else { - /* Avoid a malloc/free by using buf[]. */ - STRLCPY(buf, key, len + 1); - akey = buf; - } - - hi = hash_find(&d->dv_hashtab, akey); - xfree(tofree); - if (HASHITEM_EMPTY(hi)) - return NULL; - return HI2DI(hi); -} - -/// Get a function from a dictionary -/// @param[out] result The address where a pointer to the wanted callback -/// will be left. -/// @return true/false on success/failure. -static bool get_dict_callback(dict_T *d, char *key, Callback *result) -{ - dictitem_T *di = dict_find(d, (uint8_t *)key, -1); - - if (di == NULL) { - result->type = kCallbackNone; - return true; - } - - if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING - && di->di_tv.v_type != VAR_PARTIAL) { - EMSG(_("Argument is not a function or function name")); - result->type = kCallbackNone; - return false; - } - - typval_T tv; - copy_tv(&di->di_tv, &tv); - set_selfdict(&tv, d); - bool res = callback_from_typval(result, &tv); - clear_tv(&tv); - return res; -} - -/// Get a string item from a dictionary. -/// -/// @param save whether memory should be allocated for the return value -/// when false a shared buffer is used, can only be used once! -/// -/// @return the entry or NULL if the entry doesn't exist. -char_u *get_dict_string(dict_T *d, char *key, bool save) -{ - dictitem_T *di; - char_u *s; - - di = dict_find(d, (char_u *)key, -1); - if (di == NULL) { - return NULL; - } - s = get_tv_string(&di->di_tv); - if (save) { - s = vim_strsave(s); - } - return s; -} - -/// Get a number item from a dictionary. -/// -/// @return the entry or 0 if the entry doesn't exist. -long get_dict_number(dict_T *d, char *key) -{ - dictitem_T *di = dict_find(d, (char_u *)key, -1); - if (di == NULL) { - return 0; - } - return get_tv_number(&di->di_tv); -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -6815,7 +5561,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) char_u *key = NULL; dictitem_T *item; char_u *start = skipwhite(*arg + 1); - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; /* * First check if it's not a curly-braces thing: {expr}. @@ -6832,7 +5578,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) } if (evaluate) { - d = dict_alloc(); + d = tv_dict_alloc(); } tvkey.v_type = VAR_UNKNOWN; tv.v_type = VAR_UNKNOWN; @@ -6843,38 +5589,39 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; if (**arg != ':') { EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg); - clear_tv(&tvkey); + tv_clear(&tvkey); goto failret; } if (evaluate) { - key = get_tv_string_buf_chk(&tvkey, buf); + key = (char_u *)tv_get_string_buf_chk(&tvkey, buf); if (key == NULL) { - // "key" is NULL when get_tv_string_buf_chk() gave an errmsg - clear_tv(&tvkey); + // "key" is NULL when tv_get_string_buf_chk() gave an errmsg + tv_clear(&tvkey); goto failret; } } *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, evaluate) == FAIL) { /* recursive! */ - if (evaluate) - clear_tv(&tvkey); + if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! + if (evaluate) { + tv_clear(&tvkey); + } goto failret; } if (evaluate) { - item = dict_find(d, key, -1); + item = tv_dict_find(d, (const char *)key, -1); if (item != NULL) { EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key); - clear_tv(&tvkey); - clear_tv(&tv); + tv_clear(&tvkey); + tv_clear(&tv); goto failret; } - item = dictitem_alloc(key); - clear_tv(&tvkey); + item = tv_dict_item_alloc((const char *)key); + tv_clear(&tvkey); item->di_tv = tv; item->di_tv.v_lock = 0; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); } } @@ -6891,7 +5638,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg); failret: if (evaluate) { - dict_free(d); + tv_dict_free(d); } return FAIL; } @@ -7166,7 +5913,7 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate) ++*arg; name = *arg; - len = get_env_len(arg); + len = get_env_len((const char_u **)arg); if (evaluate) { if (len == 0) { @@ -7389,8 +6136,9 @@ get_func_tv ( } } - while (--argcount >= 0) - clear_tv(&argvars[argcount]); + while (--argcount >= 0) { + tv_clear(&argvars[argcount]); + } *arg = skipwhite(argp); return ret; @@ -7508,7 +6256,7 @@ bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) /// Also returns OK when an error was encountered while executing the function. int call_func( - char_u *funcname, // name of the function + const char_u *funcname, // name of the function int len, // length of "name" typval_T *rettv, // return value goes here int argcount_in, // number of "argvars" @@ -7557,7 +6305,7 @@ call_func( } if (error == ERROR_NONE && partial->pt_argc > 0) { for (argv_clear = 0; argv_clear < partial->pt_argc; argv_clear++) { - copy_tv(&partial->pt_argv[argv_clear], &argv[argv_clear]); + tv_copy(&partial->pt_argv[argv_clear], &argv[argv_clear]); } for (int i = 0; i < argcount_in; i++) { argv[i + argv_clear] = argvars_in[i]; @@ -7686,7 +6434,7 @@ call_func( } while (argv_clear > 0) { - clear_tv(&argv[--argv_clear]); + tv_clear(&argv[--argv_clear]); } xfree(tofree); xfree(name); @@ -7730,24 +6478,6 @@ static int non_zero_arg(typval_T *argvars) */ -/* - * Get the float value of "argvars[0]" into "f". - * Returns FAIL when the argument is not a Number or Float. - */ -static inline int get_float_arg(typval_T *argvars, float_T *f) -{ - if (argvars[0].v_type == VAR_FLOAT) { - *f = argvars[0].vval.v_float; - return OK; - } - if (argvars[0].v_type == VAR_NUMBER) { - *f = (float_T)argvars[0].vval.v_number; - return OK; - } - EMSG(_("E808: Number or Float required")); - return FAIL; -} - // Apply a floating point C function on a typval with one float_T. // // Some versions of glibc on i386 have an optimization that makes it harder to @@ -7759,7 +6489,7 @@ static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T (*function)(float_T) = (float_T (*)(float_T))fptr; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) { + if (tv_get_float_chk(argvars, &f)) { rettv->vval.v_float = function(f); } else { rettv->vval.v_float = 0.0; @@ -7802,15 +6532,16 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_op_wrapper(argvars, rettv, (FunPtr)&fabs); } else { varnumber_T n; - int error = FALSE; + bool error = false; - n = get_tv_number_chk(&argvars[0], &error); - if (error) + n = tv_get_number_chk(&argvars[0], &error); + if (error) { rettv->vval.v_number = -1; - else if (n > 0) + } else if (n > 0) { rettv->vval.v_number = n; - else + } else { rettv->vval.v_number = -n; + } } } @@ -7827,11 +6558,12 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t arg_errmsg_len = strlen(arg_errmsg); if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { - list_append_tv(l, &argvars[1]); - copy_tv(&argvars[0], rettv); + tv_list_append_tv(l, &argvars[1]); + tv_copy(&argvars[0], rettv); } - } else + } else { EMSG(_(e_listreq)); + } } /* @@ -7839,8 +6571,8 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_and(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - & get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + & tv_get_number_chk(&argvars[1], NULL); } @@ -7858,7 +6590,6 @@ static void f_api_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) { long lnum; - char_u *line; list_T *l = NULL; listitem_T *li = NULL; typval_T *tv; @@ -7871,7 +6602,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) u_sync(TRUE); } - lnum = get_tv_lnum(argvars); + lnum = tv_get_lnum(argvars); if (lnum >= 0 && lnum <= curbuf->b_ml.ml_line_count && u_save(lnum, lnum + 1) == OK) { @@ -7882,21 +6613,23 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = l->lv_first; } for (;; ) { - if (l == NULL) - tv = &argvars[1]; /* append a string */ - else if (li == NULL) - break; /* end of list */ - else - tv = &li->li_tv; /* append item from list */ - line = get_tv_string_chk(tv); - if (line == NULL) { /* type error */ - rettv->vval.v_number = 1; /* Failed */ + if (l == NULL) { + tv = &argvars[1]; // Append a string. + } else if (li == NULL) { + break; // End of list. + } else { + tv = &li->li_tv; // Append item from list. + } + const char *const line = tv_get_string_chk(tv); + if (line == NULL) { // Type error. + rettv->vval.v_number = 1; // Failed. break; } - ml_append(lnum + added, line, (colnr_T)0, FALSE); - ++added; - if (l == NULL) + ml_append(lnum + added, (char_u *)line, (colnr_T)0, false); + added++; + if (l == NULL) { break; + } li = li->li_next; } @@ -7941,16 +6674,19 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) int idx; if (argvars[0].v_type != VAR_UNKNOWN) { - idx = get_tv_number_chk(&argvars[0], NULL); - if (idx >= 0 && idx < ARGCOUNT) - rettv->vval.v_string = vim_strsave(alist_name(&ARGLIST[idx])); - else + idx = tv_get_number_chk(&argvars[0], NULL); + if (idx >= 0 && idx < ARGCOUNT) { + rettv->vval.v_string = (char_u *)xstrdup( + (const char *)alist_name(&ARGLIST[idx])); + } else { rettv->vval.v_string = NULL; + } rettv->v_type = VAR_STRING; } else { - rettv_list_alloc(rettv); - for (idx = 0; idx < ARGCOUNT; ++idx) { - list_append_string(rettv->vval.v_list, alist_name(&ARGLIST[idx]), -1); + tv_list_alloc_ret(rettv); + for (idx = 0; idx < ARGCOUNT; idx++) { + tv_list_append_string(rettv->vval.v_list, + (const char *)alist_name(&ARGLIST[idx]), -1); } } } @@ -8022,10 +6758,10 @@ static void assert_error(garray_T *gap) if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) { // Make sure v:errors is a list. - set_vim_var_list(VV_ERRORS, list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc()); } - list_append_string(vimvars[VV_ERRORS].vv_list, - gap->ga_data, gap->ga_len); + tv_list_append_string(vimvars[VV_ERRORS].vv_list, + (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); } static void assert_equal_common(typval_T *argvars, assert_type_T atype) @@ -8059,7 +6795,7 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr) { garray_T ga; - char *error = (char *)get_tv_string_chk(&argvars[0]); + const char *const error = tv_get_string_chk(&argvars[0]); if (vimvars[VV_EXCEPTION].vv_str == NULL) { prepare_assert_error(&ga); ga_concat(&ga, (char_u *)"v:exception is not set"); @@ -8078,22 +6814,22 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "assert_fails(cmd [, error])" function static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *cmd = get_tv_string_chk(&argvars[0]); + const char *const cmd = tv_get_string_chk(&argvars[0]); garray_T ga; called_emsg = false; suppress_errthrow = true; emsg_silent = true; - do_cmdline_cmd((char *)cmd); + do_cmdline_cmd(cmd); if (!called_emsg) { prepare_assert_error(&ga); - ga_concat(&ga, (char_u *)"command did not fail: "); - ga_concat(&ga, cmd); + ga_concat(&ga, (const char_u *)"command did not fail: "); + ga_concat(&ga, (const char_u *)cmd); assert_error(&ga); ga_clear(&ga); } else if (argvars[1].v_type != VAR_UNKNOWN) { - char_u buf[NUMBUFLEN]; - char *error = (char *)get_tv_string_buf_chk(&argvars[1], buf); + char buf[NUMBUFLEN]; + const char *const error = tv_get_string_buf_chk(&argvars[1], buf); if (error == NULL || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) { @@ -8114,10 +6850,10 @@ static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) void assert_inrange(typval_T *argvars) { - int error = (int)false; - varnumber_T lower = get_tv_number_chk(&argvars[0], &error); - varnumber_T upper = get_tv_number_chk(&argvars[1], &error); - varnumber_T actual = get_tv_number_chk(&argvars[2], &error); + bool error = false; + const varnumber_T lower = tv_get_number_chk(&argvars[0], &error); + const varnumber_T upper = tv_get_number_chk(&argvars[1], &error); + const varnumber_T actual = tv_get_number_chk(&argvars[2], &error); if (error) { return; @@ -8127,8 +6863,9 @@ void assert_inrange(typval_T *argvars) prepare_assert_error(&ga); char msg[55]; - vim_snprintf(msg, sizeof(msg), "range %" PRId64 " - %" PRId64 ",", - (int64_t)lower, (int64_t)upper); + vim_snprintf(msg, sizeof(msg), + "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",", + lower, upper); fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2], ASSERT_INRANGE); assert_error(&ga); @@ -8139,11 +6876,11 @@ void assert_inrange(typval_T *argvars) // Common for assert_true() and assert_false(). static void assert_bool(typval_T *argvars, bool is_true) { - int error = (int)false; + bool error = false; garray_T ga; if ((argvars[0].v_type != VAR_NUMBER - || (get_tv_number_chk(&argvars[0], &error) == 0) == is_true + || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true || error) && (argvars[0].v_type != VAR_SPECIAL || (argvars[0].vval.v_special @@ -8167,14 +6904,15 @@ static void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void assert_match_common(typval_T *argvars, assert_type_T atype) { - char_u buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - char_u *pat = get_tv_string_buf_chk(&argvars[0], buf1); - char_u *text = get_tv_string_buf_chk(&argvars[1], buf2); + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1); + const char *const text = tv_get_string_buf_chk(&argvars[1], buf2); if (pat == NULL || text == NULL) { EMSG(_(e_invarg)); - } else if (pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) { + } else if (pattern_match((char_u *)pat, (char_u *)text, false) + != (atype == ASSERT_MATCH)) { garray_T ga; prepare_assert_error(&ga); fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype); @@ -8212,14 +6950,15 @@ static void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = atan2(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* @@ -8361,8 +7100,7 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int error = false; - char_u *name; + bool error = false; rettv->vval.v_number = -1; if (!tv_check_str_or_nr(&argvars[0])) { @@ -8374,13 +7112,14 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) // If the buffer isn't found and the second argument is not zero create a // new buffer. + const char *name; if (buf == NULL && argvars[1].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[1], &error) != 0 + && tv_get_number_chk(&argvars[1], &error) != 0 && !error - && (name = get_tv_string_chk(&argvars[0])) != NULL + && (name = tv_get_string_chk(&argvars[0])) != NULL && !error) { - buf = buflist_new(name, NULL, (linenr_T)1, 0); + buf = buflist_new((char_u *)name, NULL, 1, 0); } if (buf != NULL) { @@ -8434,7 +7173,7 @@ static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long boff = get_tv_number(&argvars[0]) - 1; + long boff = tv_get_number(&argvars[0]) - 1; if (boff < 0) { rettv->vval.v_number = -1; } else { @@ -8445,24 +7184,23 @@ static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void byteidx(typval_T *argvars, typval_T *rettv, int comp) { - char_u *t; - char_u *str; - long idx; - - str = get_tv_string_chk(&argvars[0]); - idx = get_tv_number_chk(&argvars[1], NULL); + const char *const str = tv_get_string_chk(&argvars[0]); + varnumber_T idx = tv_get_number_chk(&argvars[1], NULL); rettv->vval.v_number = -1; - if (str == NULL || idx < 0) + if (str == NULL || idx < 0) { return; + } - t = str; + const char *t = str; for (; idx > 0; idx--) { - if (*t == NUL) /* EOL reached */ + if (*t == NUL) { // EOL reached. return; - if (enc_utf8 && comp) - t += utf_ptr2len(t); - else - t += (*mb_ptr2len)(t); + } + if (enc_utf8 && comp) { + t += utf_ptr2len((const char_u *)t); + } else { + t += (*mb_ptr2len)((const char_u *)t); + } } rettv->vval.v_number = (varnumber_T)(t - str); } @@ -8501,7 +7239,7 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, /* Make a copy of each argument. This is needed to be able to set * v_lock to VAR_FIXED in the copy without changing the original list. */ - copy_tv(&item->li_tv, &argv[argc++]); + tv_copy(&item->li_tv, &argv[argc++]); } if (item == NULL) { @@ -8510,9 +7248,10 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, &dummy, true, partial, selfdict); } - /* Free the arguments. */ - while (argc > 0) - clear_tv(&argv[--argc]); + // Free the arguments. + while (argc > 0) { + tv_clear(&argv[--argc]); + } return r; } @@ -8520,24 +7259,24 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, /// "call(func, arglist [, dict])" function static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *func; - partial_T *partial = NULL; - dict_T *selfdict = NULL; - if (argvars[1].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } - if (argvars[1].vval.v_list == NULL) + if (argvars[1].vval.v_list == NULL) { return; + } + char_u *func; + partial_T *partial = NULL; + dict_T *selfdict = NULL; if (argvars[0].v_type == VAR_FUNC) { func = argvars[0].vval.v_string; } else if (argvars[0].v_type == VAR_PARTIAL) { partial = argvars[0].vval.v_partial; func = partial_name(partial); } else { - func = get_tv_string(&argvars[0]); + func = (char_u *)tv_get_string(&argvars[0]); } if (*func == NUL) { return; // type error or empty name @@ -8570,15 +7309,15 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (has_mbyte) { int utf8 = 0; - if (argvars[1].v_type != VAR_UNKNOWN) - utf8 = get_tv_number_chk(&argvars[1], NULL); + if (argvars[1].v_type != VAR_UNKNOWN) { + utf8 = tv_get_number_chk(&argvars[1], NULL); + } - if (utf8) - rettv->vval.v_number = (*utf_ptr2char)(get_tv_string(&argvars[0])); - else - rettv->vval.v_number = (*mb_ptr2char)(get_tv_string(&argvars[0])); - } else - rettv->vval.v_number = get_tv_string(&argvars[0])[0]; + rettv->vval.v_number = (utf8 ? *utf_ptr2char : *mb_ptr2char)( + (const char_u *)tv_get_string(&argvars[0])); + } else { + rettv->vval.v_number = (uint8_t)(tv_get_string(&argvars[0])[0]); + } } /* @@ -8590,7 +7329,7 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) linenr_T lnum; pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); + lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = lnum; rettv->vval.v_number = get_c_indent(); @@ -8649,8 +7388,6 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int startcol; - if ((State & INSERT) == 0) { EMSG(_("E785: complete() can only be used in Insert mode")); return; @@ -8666,9 +7403,10 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - startcol = get_tv_number_chk(&argvars[0], NULL); - if (startcol <= 0) + const int startcol = tv_get_number_chk(&argvars[0], NULL); + if (startcol <= 0) { return; + } set_completion(startcol - 1, argvars[1].vval.v_list); } @@ -8699,47 +7437,51 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *message; - char_u *buttons = NULL; - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; + char buf[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *message; + const char *buttons = NULL; int def = 1; int type = VIM_GENERIC; - char_u *typestr; - int error = FALSE; + const char *typestr; + bool error = false; - message = get_tv_string_chk(&argvars[0]); - if (message == NULL) - error = TRUE; + message = tv_get_string_chk(&argvars[0]); + if (message == NULL) { + error = true; + } if (argvars[1].v_type != VAR_UNKNOWN) { - buttons = get_tv_string_buf_chk(&argvars[1], buf); - if (buttons == NULL) - error = TRUE; + buttons = tv_get_string_buf_chk(&argvars[1], buf); + if (buttons == NULL) { + error = true; + } if (argvars[2].v_type != VAR_UNKNOWN) { - def = get_tv_number_chk(&argvars[2], &error); + def = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - typestr = get_tv_string_buf_chk(&argvars[3], buf2); - if (typestr == NULL) - error = TRUE; - else { + typestr = tv_get_string_buf_chk(&argvars[3], buf2); + if (typestr == NULL) { + error = true; + } else { switch (TOUPPER_ASC(*typestr)) { - case 'E': type = VIM_ERROR; break; - case 'Q': type = VIM_QUESTION; break; - case 'I': type = VIM_INFO; break; - case 'W': type = VIM_WARNING; break; - case 'G': type = VIM_GENERIC; break; + case 'E': type = VIM_ERROR; break; + case 'Q': type = VIM_QUESTION; break; + case 'I': type = VIM_INFO; break; + case 'W': type = VIM_WARNING; break; + case 'G': type = VIM_GENERIC; break; } } } } } - if (buttons == NULL || *buttons == NUL) - buttons = (char_u *)_("&Ok"); + if (buttons == NULL || *buttons == NUL) { + buttons = _("&Ok"); + } - if (!error) - rettv->vval.v_number = do_dialog(type, NULL, message, buttons, - def, NULL, FALSE); + if (!error) { + rettv->vval.v_number = do_dialog( + type, NULL, (char_u *)message, (char_u *)buttons, def, NULL, false); + } } /* @@ -8766,15 +7508,16 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL) { li = l->lv_first; if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; - ic = get_tv_number_chk(&argvars[2], &error); + ic = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - idx = get_tv_number_chk(&argvars[3], &error); + idx = tv_get_number_chk(&argvars[3], &error); if (!error) { - li = list_find(l, idx); - if (li == NULL) + li = tv_list_find(l, idx); + if (li == NULL) { EMSGN(_(e_listidx), idx); + } } } if (error) @@ -8791,20 +7534,22 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) hashitem_T *hi; if ((d = argvars[0].vval.v_dict) != NULL) { - int error = FALSE; + bool error = false; if (argvars[2].v_type != VAR_UNKNOWN) { - ic = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) + ic = tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { EMSG(_(e_invarg)); + } } todo = error ? 0 : (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { - --todo; - if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE)) - ++n; + todo--; + if (tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &argvars[1], ic, false)) { + n++; + } } } } @@ -8821,19 +7566,21 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int num = 0; - char_u *dbpath = NULL; - char_u *prepend = NULL; - char_u buf[NUMBUFLEN]; + const char *dbpath = NULL; + const char *prepend = NULL; + char buf[NUMBUFLEN]; if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { - num = (int)get_tv_number(&argvars[0]); - dbpath = get_tv_string(&argvars[1]); - if (argvars[2].v_type != VAR_UNKNOWN) - prepend = get_tv_string_buf(&argvars[2], buf); + num = (int)tv_get_number(&argvars[0]); + dbpath = tv_get_string(&argvars[1]); + if (argvars[2].v_type != VAR_UNKNOWN) { + prepend = tv_get_string_buf(&argvars[2], buf); + } } - rettv->vval.v_number = cs_connection(num, dbpath, prepend); + rettv->vval.v_number = cs_connection(num, (char_u *)dbpath, + (char_u *)prepend); } /// "cursor(lnum, col)" function, or @@ -8866,10 +7613,10 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) set_curswant = false; } } else { - line = get_tv_lnum(argvars); - col = get_tv_number_chk(&argvars[1], NULL); + line = tv_get_lnum(argvars); + col = tv_get_number_chk(&argvars[1], NULL); if (argvars[2].v_type != VAR_UNKNOWN) { - coladd = get_tv_number_chk(&argvars[2], NULL); + coladd = tv_get_number_chk(&argvars[2], NULL); } } if (line < 0 || col < 0 @@ -8902,10 +7649,11 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int noref = 0; - if (argvars[1].v_type != VAR_UNKNOWN) - noref = get_tv_number_chk(&argvars[1], NULL); + if (argvars[1].v_type != VAR_UNKNOWN) { + noref = tv_get_number_chk(&argvars[1], NULL); + } if (noref < 0 || noref > 1) { - EMSG(_(e_invarg)); + emsgf(_(e_invarg)); } else { var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0 ? get_copyID() @@ -8916,34 +7664,32 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "delete()" function static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u nbuf[NUMBUFLEN]; - char_u *name; - char_u *flags; - rettv->vval.v_number = -1; if (check_restricted() || check_secure()) { return; } - name = get_tv_string(&argvars[0]); + const char *const name = tv_get_string(&argvars[0]); if (name == NULL || *name == NUL) { EMSG(_(e_invarg)); return; } + char nbuf[NUMBUFLEN]; + const char *flags; if (argvars[1].v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf(&argvars[1], nbuf); + flags = tv_get_string_buf(&argvars[1], nbuf); } else { - flags = (char_u *)""; + flags = ""; } if (*flags == NUL) { // delete a file - rettv->vval.v_number = os_remove((char *)name) == 0 ? 0 : -1; - } else if (STRCMP(flags, "d") == 0) { + rettv->vval.v_number = os_remove(name) == 0 ? 0 : -1; + } else if (strcmp(flags, "d") == 0) { // delete an empty directory - rettv->vval.v_number = os_rmdir((char *)name) == 0 ? 0 : -1; - } else if (STRCMP(flags, "rf") == 0) { + rettv->vval.v_number = os_rmdir(name) == 0 ? 0 : -1; + } else if (strcmp(flags, "rf") == 0) { // delete a directory recursively rettv->vval.v_number = delete_recursive(name); } else { @@ -8959,35 +7705,34 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[0].v_type != VAR_DICT) { - EMSG2(e_invarg2, "dict"); + emsgf(_(e_invarg2), "dict"); + return; + } else if (argvars[0].vval.v_dict == NULL) { + const char *const arg_errmsg = _("dictwatcheradd() argument"); + const size_t arg_errmsg_len = strlen(arg_errmsg); + emsgf(_(e_readonlyvar), (int)arg_errmsg_len, arg_errmsg); return; } if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { - EMSG2(e_invarg2, "key"); + emsgf(_(e_invarg2), "key"); return; } - char *key_pattern = (char *)get_tv_string_chk(argvars + 1); - assert(key_pattern); - const size_t key_len = STRLEN(argvars[1].vval.v_string); - - if (key_len == 0) { - EMSG(_(e_emptykey)); + const char *const key_pattern = tv_get_string_chk(argvars + 1); + if (key_pattern == NULL) { return; } + const size_t key_pattern_len = strlen(key_pattern); Callback callback; if (!callback_from_typval(&callback, &argvars[2])) { - EMSG2(e_invarg2, "funcref"); + emsgf(_(e_invarg2), "funcref"); return; } - DictWatcher *watcher = xmalloc(sizeof(DictWatcher)); - watcher->key_pattern = xmemdupz(key_pattern, key_len); - watcher->callback = callback; - watcher->busy = false; - QUEUE_INSERT_TAIL(&argvars[0].vval.v_dict->watchers, &watcher->node); + tv_dict_watcher_add(argvars[0].vval.v_dict, key_pattern, key_pattern_len, + callback); } // dictwatcherdel(dict, key, funcref) function @@ -8998,26 +7743,17 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[0].v_type != VAR_DICT) { - EMSG2(e_invarg2, "dict"); - return; - } - - if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { - EMSG2(e_invarg2, "key"); + emsgf(_(e_invarg2), "dict"); return; } if (argvars[2].v_type != VAR_FUNC && argvars[2].v_type != VAR_STRING) { - EMSG2(e_invarg2, "funcref"); + emsgf(_(e_invarg2), "funcref"); return; } - char *key_pattern = (char *)get_tv_string_chk(argvars + 1); - assert(key_pattern); - const size_t key_len = STRLEN(argvars[1].vval.v_string); - - if (key_len == 0) { - EMSG(_(e_emptykey)); + const char *const key_pattern = tv_get_string_chk(argvars + 1); + if (key_pattern == NULL) { return; } @@ -9026,28 +7762,12 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - dict_T *dict = argvars[0].vval.v_dict; - QUEUE *w = NULL; - DictWatcher *watcher = NULL; - bool matched = false; - QUEUE_FOREACH(w, &dict->watchers) { - watcher = dictwatcher_node_data(w); - if (callback_equal(&watcher->callback, &callback) - && !strcmp(watcher->key_pattern, key_pattern)) { - matched = true; - break; - } - } - - callback_free(&callback); - - if (!matched) { + if (!tv_dict_watcher_remove(argvars[0].vval.v_dict, key_pattern, + strlen(key_pattern), callback)) { EMSG("Couldn't find a watcher matching key and callback"); - return; } - QUEUE_REMOVE(w); - dictwatcher_free(watcher); + callback_free(&callback); } /* @@ -9063,7 +7783,7 @@ static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = diff_check_fill(curwin, get_tv_lnum(argvars)); + rettv->vval.v_number = diff_check_fill(curwin, tv_get_lnum(argvars)); } /* @@ -9071,7 +7791,7 @@ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum = get_tv_lnum(argvars); + linenr_T lnum = tv_get_lnum(argvars); static linenr_T prev_lnum = 0; static int changedtick = 0; static int fnum = 0; @@ -9106,11 +7826,12 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (hlID == HLF_CHD || hlID == HLF_TXD) { - col = get_tv_number(&argvars[1]) - 1; /* ignore type error in {col} */ - if (col >= change_start && col <= change_end) - hlID = HLF_TXD; /* changed text */ - else - hlID = HLF_CHD; /* changed line */ + col = tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}. + if (col >= change_start && col <= change_end) { + hlID = HLF_TXD; // Changed text. + } else { + hlID = HLF_CHD; // Changed line. + } } rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID; } @@ -9161,10 +7882,11 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; - rettv->vval.v_string = vim_strsave_escaped(get_tv_string(&argvars[0]), - get_tv_string_buf(&argvars[1], buf)); + rettv->vval.v_string = vim_strsave_escaped( + (const char_u *)tv_get_string(&argvars[0]), + (const char_u *)tv_get_string_buf(&argvars[1], buf)); rettv->v_type = VAR_STRING; } @@ -9173,16 +7895,15 @@ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; - - s = get_tv_string_chk(&argvars[0]); - if (s != NULL) - s = skipwhite(s); + const char *s = tv_get_string_chk(&argvars[0]); + if (s != NULL) { + s = (const char *)skipwhite((const char_u *)s); + } - char_u *p = s; - if (s == NULL || eval1(&s, rettv, TRUE) == FAIL) { - if (p != NULL && !aborting()) { - EMSG2(_(e_invexpr2), p); + const char *const expr_start = s; + if (s == NULL || eval1((char_u **)&s, rettv, true) == FAIL) { + if (expr_start != NULL && !aborting()) { + EMSG2(_(e_invexpr2), expr_start); } need_clr_eos = FALSE; rettv->v_type = VAR_NUMBER; @@ -9205,91 +7926,92 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *name = get_tv_string(&argvars[0]); + const char *name = tv_get_string(&argvars[0]); // Check in $PATH and also check directly if there is a directory name - rettv->vval.v_number = os_can_exe(name, NULL, true) - || (gettail_dir(name) != name && os_can_exe(name, NULL, false)); + rettv->vval.v_number = ( + os_can_exe((const char_u *)name, NULL, true) + || (gettail_dir(name) != name + && os_can_exe((const char_u *)name, NULL, false))); } -static char_u * get_list_line(int c, void *cookie, int indent) +static char_u *get_list_line(int c, void *cookie, int indent) { - listitem_T **p = (listitem_T **)cookie; - listitem_T *item = *p; - char_u buf[NUMBUFLEN]; - char_u *s; + const listitem_T **const p = (const listitem_T **)cookie; + const listitem_T *item = *p; if (item == NULL) { return NULL; } - s = get_tv_string_buf_chk(&item->li_tv, buf); + char buf[NUMBUFLEN]; + const char *const s = tv_get_string_buf_chk(&item->li_tv, buf); *p = item->li_next; - return s == NULL ? NULL : vim_strsave(s); + return (char_u *)(s == NULL ? NULL : xstrdup(s)); } // "execute(command)" function static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int save_msg_silent = msg_silent; - int save_emsg_silent = emsg_silent; - bool save_emsg_noredir = emsg_noredir; - garray_T *save_capture_ga = capture_ga; + const int save_msg_silent = msg_silent; + const int save_emsg_silent = emsg_silent; + const bool save_emsg_noredir = emsg_noredir; + garray_T *const save_capture_ga = capture_ga; - if (check_secure()) { - return; - } + if (check_secure()) { + return; + } - if (argvars[1].v_type != VAR_UNKNOWN) { - char_u buf[NUMBUFLEN]; - char_u *s = get_tv_string_buf_chk(&argvars[1], buf); + if (argvars[1].v_type != VAR_UNKNOWN) { + char buf[NUMBUFLEN]; + const char *const s = tv_get_string_buf_chk(&argvars[1], buf); - if (s == NULL) { - return; - } - if (STRNCMP(s, "silent", 6) == 0) { - msg_silent++; - } - if (STRCMP(s, "silent!") == 0) { - emsg_silent = true; - emsg_noredir = true; - } - } else { + if (s == NULL) { + return; + } + if (strncmp(s, "silent", 6) == 0) { msg_silent++; } - - garray_T capture_local; - ga_init(&capture_local, (int)sizeof(char), 80); - capture_ga = &capture_local; - - if (argvars[0].v_type != VAR_LIST) { - do_cmdline_cmd((char *)get_tv_string(&argvars[0])); - } else if (argvars[0].vval.v_list != NULL) { - list_T *list = argvars[0].vval.v_list; - list->lv_refcount++; - listitem_T *item = list->lv_first; - do_cmdline(NULL, get_list_line, (void *)&item, - DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); - list->lv_refcount--; + if (strcmp(s, "silent!") == 0) { + emsg_silent = true; + emsg_noredir = true; } - msg_silent = save_msg_silent; - emsg_silent = save_emsg_silent; - emsg_noredir = save_emsg_noredir; + } else { + msg_silent++; + } - ga_append(capture_ga, NUL); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(capture_ga->ga_data); - ga_clear(capture_ga); + garray_T capture_local; + ga_init(&capture_local, (int)sizeof(char), 80); + capture_ga = &capture_local; + + if (argvars[0].v_type != VAR_LIST) { + do_cmdline_cmd(tv_get_string(&argvars[0])); + } else if (argvars[0].vval.v_list != NULL) { + list_T *const list = argvars[0].vval.v_list; + list->lv_refcount++; + listitem_T *const item = list->lv_first; + do_cmdline(NULL, get_list_line, (void *)&item, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); + list->lv_refcount--; + } + msg_silent = save_msg_silent; + emsg_silent = save_emsg_silent; + emsg_noredir = save_emsg_noredir; + + ga_append(capture_ga, NUL); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(capture_ga->ga_data); + ga_clear(capture_ga); - capture_ga = save_capture_ga; + capture_ga = save_capture_ga; } /// "exepath()" function static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *arg = get_tv_string(&argvars[0]); + const char *arg = tv_get_string(&argvars[0]); char_u *path = NULL; - (void)os_can_exe(arg, &path, true); + (void)os_can_exe((const char_u *)arg, &path, true); rettv->v_type = VAR_STRING; rettv->vval.v_string = path; @@ -9303,21 +8025,21 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n = false; int len = 0; - char *p = (char *)get_tv_string(&argvars[0]); + const char *p = tv_get_string(&argvars[0]); if (*p == '$') { // Environment variable. // First try "normal" environment variables (fast). if (os_getenv(p + 1) != NULL) { n = true; } else { // Try expanding things like $VIM and ${HOME}. - p = (char *)expand_env_save((char_u *)p); - if (p != NULL && *p != '$') { + char_u *const exp = expand_env_save((char_u *)p); + if (exp != NULL && *exp != '$') { n = true; } - xfree(p); + xfree(exp); } } else if (*p == '&' || *p == '+') { // Option. - n = (get_option_tv((const char **)&p, NULL, true) == OK); + n = (get_option_tv(&p, NULL, true) == OK); if (*skipwhite((const char_u *)p) != NUL) { n = false; // Trailing garbage. } @@ -9345,9 +8067,9 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = (get_var_tv(name, len, &tv, NULL, false, true) == OK); if (n) { // Handle d.key, l[idx], f(expr). - n = (handle_subscript((const char **)&p, &tv, true, false) == OK); + n = (handle_subscript(&p, &tv, true, false) == OK); if (n) { - clear_tv(&tv); + tv_clear(&tv); } } } @@ -9365,32 +8087,31 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; size_t len; char_u *errormsg; int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; expand_T xpc; - int error = FALSE; - char_u *result; + bool error = false; + char_u *result; rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[2], &error) + && tv_get_number_chk(&argvars[2], &error) && !error) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } - s = get_tv_string(&argvars[0]); + const char *s = tv_get_string(&argvars[0]); if (*s == '%' || *s == '#' || *s == '<') { - ++emsg_off; - result = eval_vars(s, s, &len, NULL, &errormsg, NULL); - --emsg_off; + emsg_off++; + result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL); + emsg_off--; if (rettv->v_type == VAR_LIST) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (result != NULL) { - list_append_string(rettv->vval.v_list, result, -1); + tv_list_append_string(rettv->vval.v_list, (const char *)result, -1); } } else rettv->vval.v_string = result; @@ -9398,93 +8119,29 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* When the optional second argument is non-zero, don't remove matches * for 'wildignore' and don't put matches for 'suffixes' at the end. */ if (argvars[1].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[1], &error)) + && tv_get_number_chk(&argvars[1], &error)) { options |= WILD_KEEP_ALL; + } if (!error) { ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; - if (p_wic) + if (p_wic) { options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = ExpandOne(&xpc, s, NULL, - options, WILD_ALL); - else { - rettv_list_alloc(rettv); - ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); + } + if (rettv->v_type == VAR_STRING) { + rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options, + WILD_ALL); + } else { + tv_list_alloc_ret(rettv); + ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, + (const char *)xpc.xp_files[i], -1); } ExpandCleanup(&xpc); } - } else + } else { rettv->vval.v_string = NULL; - } -} - -/* - * Go over all entries in "d2" and add them to "d1". - * When "action" is "error" then a duplicate key is an error. - * When "action" is "force" then a duplicate key is overwritten. - * Otherwise duplicate keys are ignored ("action" is "keep"). - */ -void dict_extend(dict_T *d1, dict_T *d2, char_u *action) -{ - dictitem_T *di1; - hashitem_T *hi2; - int todo; - bool watched = is_watched(d1); - const char *const arg_errmsg = _("extend() argument"); - const size_t arg_errmsg_len = strlen(arg_errmsg); - - todo = (int)d2->dv_hashtab.ht_used; - for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { - if (!HASHITEM_EMPTY(hi2)) { - --todo; - di1 = dict_find(d1, hi2->hi_key, -1); - if (d1->dv_scope != 0) { - /* Disallow replacing a builtin function in l: and g:. - * Check the key to be valid when adding to any - * scope. */ - if (d1->dv_scope == VAR_DEF_SCOPE - && HI2DI(hi2)->di_tv.v_type == VAR_FUNC - && var_check_func_name(hi2->hi_key, - di1 == NULL)) - break; - if (!valid_varname(hi2->hi_key)) - break; - } - if (di1 == NULL) { - di1 = dictitem_copy(HI2DI(hi2)); - if (dict_add(d1, di1) == FAIL) { - dictitem_free(di1); - } - - if (watched) { - dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, NULL); - } - } else if (*action == 'e') { - EMSG2(_("E737: Key already exists: %s"), hi2->hi_key); - break; - } else if (*action == 'f' && HI2DI(hi2) != di1) { - typval_T oldtv; - - if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) - || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { - break; - } - - if (watched) { - copy_tv(&di1->di_tv, &oldtv); - } - - clear_tv(&di1->di_tv); - copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); - - if (watched) { - dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, &oldtv); - clear_tv(&oldtv); - } - } } } } @@ -9499,24 +8156,30 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t arg_errmsg_len = strlen(arg_errmsg); if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { - list_T *l1, *l2; - listitem_T *item; long before; - int error = FALSE; - - l1 = argvars[0].vval.v_list; - l2 = argvars[1].vval.v_list; - if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len) - && l2 != NULL) { + bool error = false; + + list_T *const l1 = argvars[0].vval.v_list; + list_T *const l2 = argvars[1].vval.v_list; + if (l1 == NULL) { + const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, arg_errmsg_len); + (void)locked; + assert(locked == true); + } else if (l2 == NULL) { + // Do nothing + tv_copy(&argvars[0], rettv); + } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len)) { + listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { - before = get_tv_number_chk(&argvars[2], &error); - if (error) - return; /* type error; errmsg already given */ + before = tv_get_number_chk(&argvars[2], &error); + if (error) { + return; // Type error; errmsg already given. + } - if (before == l1->lv_len) + if (before == l1->lv_len) { item = NULL; - else { - item = list_find(l1, before); + } else { + item = tv_list_find(l1, before); if (item == NULL) { EMSGN(_(e_listidx), before); return; @@ -9524,43 +8187,50 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else item = NULL; - list_extend(l1, l2, item); + tv_list_extend(l1, l2, item); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { - dict_T *d1, *d2; - char_u *action; - int i; - - d1 = argvars[0].vval.v_dict; - d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len) - && d2 != NULL) { - /* Check the third argument. */ + dict_T *const d1 = argvars[0].vval.v_dict; + dict_T *const d2 = argvars[1].vval.v_dict; + if (d1 == NULL) { + const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, arg_errmsg_len); + (void)locked; + assert(locked == true); + } else if (d2 == NULL) { + // Do nothing + tv_copy(&argvars[0], rettv); + } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len)) { + const char *action = "force"; + // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { - static char *(av[]) = {"keep", "force", "error"}; + const char *const av[] = { "keep", "force", "error" }; - action = get_tv_string_chk(&argvars[2]); - if (action == NULL) - return; /* type error; errmsg already given */ - for (i = 0; i < 3; ++i) - if (STRCMP(action, av[i]) == 0) + action = tv_get_string_chk(&argvars[2]); + if (action == NULL) { + return; // Type error; error message already given. + } + size_t i; + for (i = 0; i < ARRAY_SIZE(av); i++) { + if (strcmp(action, av[i]) == 0) { break; + } + } if (i == 3) { EMSG2(_(e_invarg2), action); return; } - } else - action = (char_u *)"force"; + } - dict_extend(d1, d2, action); + tv_dict_extend(d1, d2, action); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } - } else + } else { EMSG2(_(e_listdictarg), "extend()"); + } } /* @@ -9568,19 +8238,18 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *keys, *flags = NULL; - char_u nbuf[NUMBUFLEN]; - - /* This is not allowed in the sandbox. If the commands would still be - * executed in the sandbox it would be OK, but it probably happens later, - * when "sandbox" is no longer set. */ - if (check_secure()) + // This is not allowed in the sandbox. If the commands would still be + // executed in the sandbox it would be OK, but it probably happens later, + // when "sandbox" is no longer set. + if (check_secure()) { return; + } - keys = get_tv_string(&argvars[0]); - + const char *const keys = tv_get_string(&argvars[0]); + char nbuf[NUMBUFLEN]; + const char *flags = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf(&argvars[1], nbuf); + flags = tv_get_string_buf(&argvars[1], nbuf); } nvim_feedkeys(cstr_as_string((char *)keys), @@ -9590,9 +8259,9 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "filereadable()" function static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = get_tv_string(&argvars[0]); + const char *const p = tv_get_string(&argvars[0]); rettv->vval.v_number = - (*p && !os_isdir(p) && os_file_is_readable((char*)p)); + (*p && !os_isdir((const char_u *)p) && os_file_is_readable(p)); } /* @@ -9601,60 +8270,60 @@ static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *filename = (char *)get_tv_string(&argvars[0]); + const char *filename = tv_get_string(&argvars[0]); rettv->vval.v_number = os_file_is_writable(filename); } static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) { - char_u *fname; - char_u *fresult = NULL; - char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; - char_u *p; - char_u pathbuf[NUMBUFLEN]; + char_u *fresult = NULL; + char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; int count = 1; - int first = TRUE; - int error = FALSE; + bool first = true; + bool error = false; rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; - fname = get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); + char pathbuf[NUMBUFLEN]; if (argvars[1].v_type != VAR_UNKNOWN) { - p = get_tv_string_buf_chk(&argvars[1], pathbuf); - if (p == NULL) - error = TRUE; - else { - if (*p != NUL) - path = p; + const char *p = tv_get_string_buf_chk(&argvars[1], pathbuf); + if (p == NULL) { + error = true; + } else { + if (*p != NUL) { + path = (char_u *)p; + } - if (argvars[2].v_type != VAR_UNKNOWN) - count = get_tv_number_chk(&argvars[2], &error); + if (argvars[2].v_type != VAR_UNKNOWN) { + count = tv_get_number_chk(&argvars[2], &error); + } } } if (count < 0) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } if (*fname != NUL && !error) { do { if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) xfree(fresult); - fresult = find_file_in_path_option(first ? fname : NULL, - first ? STRLEN(fname) : 0, + 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 == FINDFILE_DIR ? (char_u *)"" : curbuf->b_p_sua)); - first = FALSE; - - if (fresult != NULL && rettv->v_type == VAR_LIST) - list_append_string(rettv->vval.v_list, fresult, -1); + first = false; + if (fresult != NULL && rettv->v_type == VAR_LIST) { + tv_list_append_string(rettv->vval.v_list, (const char *)fresult, -1); + } } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); } @@ -9725,7 +8394,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (!HASHITEM_EMPTY(hi)) { --todo; - di = HI2DI(hi); + di = TV_DICT_HI2DI(hi); if (map && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, arg_errmsg_len) || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len))) { @@ -9734,15 +8403,16 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); int r = filter_map_one(&di->di_tv, expr, map, &rem); - clear_tv(&vimvars[VV_KEY].vv_tv); - if (r == FAIL || did_emsg) + tv_clear(&vimvars[VV_KEY].vv_tv); + if (r == FAIL || did_emsg) { break; + } if (!map && rem) { if (var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len) || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { break; } - dictitem_remove(d, di); + tv_dict_item_remove(d, di); } } } @@ -9760,9 +8430,10 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL || did_emsg) break; - if (!map && rem) - listitem_remove(l, li); - ++idx; + if (!map && rem) { + tv_list_item_remove(l, li); + } + idx++; } } @@ -9772,23 +8443,21 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) did_emsg |= save_did_emsg; } - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) { typval_T rettv; typval_T argv[3]; - char_u buf[NUMBUFLEN]; - char_u *s; int retval = FAIL; int dummy; - copy_tv(tv, &vimvars[VV_VAL].vv_tv); + tv_copy(tv, &vimvars[VV_VAL].vv_tv); argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; if (expr->v_type == VAR_FUNC) { - s = expr->vval.v_string; + const char_u *const s = expr->vval.v_string; if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, NULL, NULL) == FAIL) { goto theend; @@ -9796,45 +8465,47 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) } else if (expr->v_type == VAR_PARTIAL) { partial_T *partial = expr->vval.v_partial; - s = partial_name(partial); + const char_u *const s = partial_name(partial); if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, partial, NULL) == FAIL) { goto theend; } } else { - s = get_tv_string_buf_chk(expr, buf); + char buf[NUMBUFLEN]; + const char *s = tv_get_string_buf_chk(expr, buf); if (s == NULL) { goto theend; } - s = skipwhite(s); - if (eval1(&s, &rettv, true) == FAIL) { + s = (const char *)skipwhite((const char_u *)s); + if (eval1((char_u **)&s, &rettv, true) == FAIL) { goto theend; } if (*s != NUL) { // check for trailing chars after expr - EMSG2(_(e_invexpr2), s); + emsgf(_(e_invexpr2), s); goto theend; } } if (map) { - /* map(): replace the list item value */ - clear_tv(tv); + // map(): replace the list item value. + tv_clear(tv); rettv.v_lock = 0; *tv = rettv; } else { - int error = FALSE; - - /* filter(): when expr is zero remove the item */ - *remp = (get_tv_number_chk(&rettv, &error) == 0); - clear_tv(&rettv); - /* On type error, nothing has been removed; return FAIL to stop the - * loop. The error message was given by get_tv_number_chk(). */ - if (error) + bool error = false; + + // filter(): when expr is zero remove the item + *remp = (tv_get_number_chk(&rettv, &error) == 0); + tv_clear(&rettv); + // On type error, nothing has been removed; return FAIL to stop the + // loop. The error message was given by tv_get_number_chk(). + if (error) { goto theend; + } } retval = OK; theend: - clear_tv(&vimvars[VV_VAL].vv_tv); + tv_clear(&vimvars[VV_VAL].vv_tv); return retval; } @@ -9869,13 +8540,14 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T f; - if (get_float_arg(argvars, &f) == OK) { - if (f < -0x7fffffff) - rettv->vval.v_number = -0x7fffffff; - else if (f > 0x7fffffff) - rettv->vval.v_number = 0x7fffffff; - else + if (tv_get_float_chk(argvars, &f)) { + if (f < VARNUMBER_MIN) { + rettv->vval.v_number = VARNUMBER_MIN; + } else if (f > VARNUMBER_MAX) { + rettv->vval.v_number = VARNUMBER_MAX; + } else { rettv->vval.v_number = (varnumber_T)f; + } } } @@ -9884,14 +8556,15 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = fmod(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* @@ -9899,8 +8572,8 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_string = vim_strsave_fnameescape( - get_tv_string(&argvars[0]), FALSE); + rettv->vval.v_string = (char_u *)vim_strsave_fnameescape( + tv_get_string(&argvars[0]), false); rettv->v_type = VAR_STRING; } @@ -9909,27 +8582,26 @@ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; - char_u *mods; - size_t usedlen = 0; + char_u *fbuf = NULL; size_t len; - char_u *fbuf = NULL; - char_u buf[NUMBUFLEN]; - - fname = get_tv_string_chk(&argvars[0]); - mods = get_tv_string_buf_chk(&argvars[1], buf); - if (fname == NULL || mods == NULL) + char buf[NUMBUFLEN]; + const char *fname = tv_get_string_chk(&argvars[0]); + const char *const mods = tv_get_string_buf_chk(&argvars[1], buf); + if (fname == NULL || mods == NULL) { fname = NULL; - else { - len = STRLEN(fname); - (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len); + } else { + len = strlen(fname); + size_t usedlen = 0; + (void)modify_fname((char_u *)mods, &usedlen, (char_u **)&fname, &fbuf, + &len); } rettv->v_type = VAR_STRING; - if (fname == NULL) + if (fname == NULL) { rettv->vval.v_string = NULL; - else - rettv->vval.v_string = vim_strnsave(fname, len); + } else { + rettv->vval.v_string = (char_u *)xmemdupz(fname, len); + } xfree(fbuf); } @@ -9939,16 +8611,16 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end) { - linenr_T lnum; - linenr_T first, last; - - lnum = get_tv_lnum(argvars); + const linenr_T lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { - if (hasFoldingWin(curwin, lnum, &first, &last, FALSE, NULL)) { - if (end) + linenr_T first; + linenr_T last; + if (hasFoldingWin(curwin, lnum, &first, &last, false, NULL)) { + if (end) { rettv->vval.v_number = (varnumber_T)last; - else + } else { rettv->vval.v_number = (varnumber_T)first; + } return; } } @@ -9976,11 +8648,10 @@ static void f_foldclosedend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { rettv->vval.v_number = foldLevel(lnum); + } } /* @@ -10044,7 +8715,6 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; char_u *text; char_u buf[51]; foldinfo_T foldinfo; @@ -10052,10 +8722,11 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - lnum = get_tv_lnum(argvars); - /* treat illegal types and illegal string values for {lnum} the same */ - if (lnum < 0) + linenr_T lnum = tv_get_lnum(argvars); + // Treat illegal types and illegal string values for {lnum} the same. + if (lnum < 0) { lnum = 0; + } fold_count = foldedCount(curwin, lnum, &foldinfo); if (fold_count > 0) { text = get_foldtext(curwin, lnum, lnum + fold_count - 1, @@ -10092,7 +8763,7 @@ static void common_function(typval_T *argvars, typval_T *rettv, s = partial_name(arg_pt); } else { // function('MyFunc', [arg], dict) - s = get_tv_string(&argvars[0]); + s = (char_u *)tv_get_string(&argvars[0]); use_string = true; } @@ -10107,7 +8778,9 @@ static void common_function(typval_T *argvars, typval_T *rettv, } if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) || (is_funcref && trans_name == NULL)) { - EMSG2(_(e_invarg2), use_string ? get_tv_string(&argvars[0]) : s); + emsgf(_(e_invarg2), (use_string + ? tv_get_string(&argvars[0]) + : (const char *)s)); // Don't check an autoload name for existence here. } else if (trans_name != NULL && (is_funcref ? find_func(trans_name) == NULL @@ -10188,13 +8861,13 @@ static void common_function(typval_T *argvars, typval_T *rettv, } int i = 0; for (; i < arg_len; i++) { - copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); } if (lv_len > 0) { for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - copy_tv(&li->li_tv, &pt->pt_argv[i++]); + tv_copy(&li->li_tv, &pt->pt_argv[i++]); } } } @@ -10255,12 +8928,13 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "garbagecollect()" function static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - /* This is postponed until we are back at the toplevel, because we may be - * using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". */ - want_garbage_collect = TRUE; + // This is postponed until we are back at the toplevel, because we may be + // using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". + want_garbage_collect = true; - if (argvars[0].v_type != VAR_UNKNOWN && get_tv_number(&argvars[0]) == 1) - garbage_collect_at_exit = TRUE; + if (argvars[0].v_type != VAR_UNKNOWN && tv_get_number(&argvars[0]) == 1) { + garbage_collect_at_exit = true; + } } /* @@ -10276,20 +8950,21 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL) { - int error = FALSE; + bool error = false; - li = list_find(l, get_tv_number_chk(&argvars[1], &error)); - if (!error && li != NULL) + li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error)); + if (!error && li != NULL) { tv = &li->li_tv; + } } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) != NULL) { - di = dict_find(d, get_tv_string(&argvars[1]), -1); - if (di != NULL) + di = tv_dict_find(d, tv_get_string(&argvars[1]), -1); + if (di != NULL) { tv = &di->di_tv; + } } - } else if (argvars[0].v_type == VAR_PARTIAL - || argvars[0].v_type == VAR_FUNC) { + } else if (tv_is_func(argvars[0])) { partial_T *pt; partial_T fref_pt; @@ -10302,33 +8977,30 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (pt != NULL) { - char_u *what = get_tv_string(&argvars[1]); - char_u *n; + const char *const what = tv_get_string(&argvars[1]); - if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) { + if (strcmp(what, "func") == 0 || strcmp(what, "name") == 0) { rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); - n = partial_name(pt); + const char *const n = (const char *)partial_name(pt); if (n == NULL) { rettv->vval.v_string = NULL; } else { - rettv->vval.v_string = vim_strsave(n); + rettv->vval.v_string = (char_u *)xstrdup(n); if (rettv->v_type == VAR_FUNC) { func_ref(rettv->vval.v_string); } } - } else if (STRCMP(what, "dict") == 0) { + } else if (strcmp(what, "dict") == 0) { rettv->v_type = VAR_DICT; rettv->vval.v_dict = pt->pt_dict; if (pt->pt_dict != NULL) { (pt->pt_dict->dv_refcount)++; } - } else if (STRCMP(what, "args") == 0) { + } else if (strcmp(what, "args") == 0) { rettv->v_type = VAR_LIST; - if (rettv_list_alloc(rettv) != NULL) { - int i; - - for (i = 0; i < pt->pt_argc; i++) { - list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); + if (tv_list_alloc_ret(rettv) != NULL) { + for (int i = 0; i < pt->pt_argc; i++) { + tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); } } } else { @@ -10341,60 +9013,62 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (tv == NULL) { - if (argvars[2].v_type != VAR_UNKNOWN) - copy_tv(&argvars[2], rettv); - } else - copy_tv(tv, rettv); + if (argvars[2].v_type != VAR_UNKNOWN) { + tv_copy(&argvars[2], rettv); + } + } else { + tv_copy(tv, rettv); + } } /// Returns information about signs placed in a buffer as list of dicts. static void get_buffer_signs(buf_T *buf, list_T *l) { for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { - dict_T *d = dict_alloc(); + dict_T *const d = tv_dict_alloc(); - dict_add_nr_str(d, "id", sign->id, NULL); - dict_add_nr_str(d, "lnum", sign->lnum, NULL); - dict_add_nr_str(d, "name", 0L, sign_typenr2name(sign->typenr)); + tv_dict_add_nr(d, S_LEN("id"), sign->id); + tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum); + tv_dict_add_str(d, S_LEN("name"), + (const char *)sign_typenr2name(sign->typenr)); - list_append_dict(l, d); + tv_list_append_dict(l, d); } } /// Returns buffer options, variables and other attributes in a dictionary. static dict_T *get_buffer_info(buf_T *buf) { - dict_T *dict = dict_alloc(); - - dict_add_nr_str(dict, "bufnr", buf->b_fnum, NULL); - dict_add_nr_str(dict, "name", 0L, - buf->b_ffname != NULL ? buf->b_ffname : (char_u *)""); - dict_add_nr_str(dict, "lnum", buflist_findlnum(buf), NULL); - dict_add_nr_str(dict, "loaded", buf->b_ml.ml_mfp != NULL, NULL); - dict_add_nr_str(dict, "listed", buf->b_p_bl, NULL); - dict_add_nr_str(dict, "changed", bufIsChanged(buf), NULL); - dict_add_nr_str(dict, "changedtick", buf->b_changedtick, NULL); - dict_add_nr_str(dict, "hidden", - buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0, - NULL); + dict_T *const dict = tv_dict_alloc(); + + tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum); + tv_dict_add_str(dict, S_LEN("name"), + buf->b_ffname != NULL ? (const char *)buf->b_ffname : ""); + tv_dict_add_nr(dict, S_LEN("lnum"), buflist_findlnum(buf)); + tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL); + tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl); + tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); + tv_dict_add_nr(dict, S_LEN("changedtick"), buf->b_changedtick); + tv_dict_add_nr(dict, S_LEN("hidden"), + buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); // Get a reference to buffer variables - dict_add_dict(dict, "variables", buf->b_vars); + tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); // List of windows displaying this buffer - list_T *windows = list_alloc(); + list_T *const windows = tv_list_alloc(); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { - list_append_number(windows, (varnumber_T)wp->handle); + tv_list_append_number(windows, (varnumber_T)wp->handle); } } - dict_add_list(dict, "windows", windows); + tv_dict_add_list(dict, S_LEN("windows"), windows); if (buf->b_signlist != NULL) { // List of signs placed in this buffer - list_T *signs = list_alloc(); + list_T *const signs = tv_list_alloc(); get_buffer_signs(buf, signs); - dict_add_list(dict, "signs", signs); + tv_dict_add_list(dict, S_LEN("signs"), signs); } return dict; @@ -10408,7 +9082,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool sel_buflisted = false; bool sel_bufloaded = false; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); // List of all the buffers or selected buffers if (argvars[0].v_type == VAR_DICT) { @@ -10419,24 +9093,25 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) filtered = true; - di = dict_find(sel_d, (char_u *)"buflisted", -1); - if (di != NULL && get_tv_number(&di->di_tv)) { + di = tv_dict_find(sel_d, S_LEN("buflisted")); + if (di != NULL && tv_get_number(&di->di_tv)) { sel_buflisted = true; } - di = dict_find(sel_d, (char_u *)"bufloaded", -1); - if (di != NULL && get_tv_number(&di->di_tv)) { + di = tv_dict_find(sel_d, S_LEN("bufloaded")); + if (di != NULL && tv_get_number(&di->di_tv)) { sel_bufloaded = true; } } } else if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one buffer. Argument specifies the buffer - (void)get_tv_number(&argvars[0]); // issue errmsg if type error - emsg_off++; - argbuf = get_buf_tv(&argvars[0], false); - emsg_off--; - if (argbuf == NULL) { - return; + if (tv_check_num(&argvars[0])) { // issue errmsg if type error + emsg_off++; + argbuf = get_buf_tv(&argvars[0], false); + emsg_off--; + if (argbuf == NULL) { + return; + } } } @@ -10450,9 +9125,9 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } - dict_T *d = get_buffer_info(buf); + dict_T *const d = get_buffer_info(buf); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (argbuf != NULL) { return; @@ -10473,7 +9148,7 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; if (retlist) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0) @@ -10494,19 +9169,40 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli if (end > buf->b_ml.ml_line_count) end = buf->b_ml.ml_line_count; while (start <= end) { - list_append_string( - rettv->vval.v_list, ml_get_buf(buf, start++, FALSE), -1); + tv_list_append_string(rettv->vval.v_list, + (const char *)ml_get_buf(buf, start++, false), -1); } } } +/// Get the line number from VimL object +/// +/// @note Unlike tv_get_lnum(), this one supports only "$" special string. +/// +/// @param[in] tv Object to get value from. Is expected to be a number or +/// a special string "$". +/// @param[in] buf Buffer to take last line number from in case tv is "$". May +/// be NULL, in this case "$" results in zero return. +/// +/// @return Line number or 0 in case of error. +static linenr_T tv_get_lnum_buf(const typval_T *const tv, + const buf_T *const buf) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (tv->v_type == VAR_STRING + && tv->vval.v_string != NULL + && tv->vval.v_string[0] == '$' + && buf != NULL) { + return buf->b_ml.ml_line_count; + } + return tv_get_number_chk(tv, NULL); +} + /* * "getbufline()" function */ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - linenr_T end; buf_T *buf = NULL; if (tv_check_str_or_nr(&argvars[0])) { @@ -10515,12 +9211,10 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg_off--; } - lnum = get_tv_lnum_buf(&argvars[1], buf); - if (argvars[2].v_type == VAR_UNKNOWN) { - end = lnum; - } else { - end = get_tv_lnum_buf(&argvars[2], buf); - } + const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf); + const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN + ? lnum + : tv_get_lnum_buf(&argvars[2], buf)); get_buffer_lines(buf, lnum, end, true, rettv); } @@ -10539,7 +9233,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) goto f_getbufvar_end; } - const char *varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *varname = tv_get_string_chk(&argvars[1]); emsg_off++; buf_T *const buf = get_buf_tv(&argvars[0], false); @@ -10569,7 +9263,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *const v = find_var_in_ht(&curbuf->b_vars->dv_hashtab, 'b', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -10582,7 +9276,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) f_getbufvar_end: if (!done && argvars[2].v_type != VAR_UNKNOWN) { // use the default value - copy_tv(&argvars[2], rettv); + tv_copy(&argvars[2], rettv); } } @@ -10592,10 +9286,9 @@ f_getbufvar_end: static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { varnumber_T n; - int error = FALSE; + bool error = false; - ++no_mapping; - ++allow_keys; + no_mapping++; for (;; ) { // Position the cursor. Needed after a message that ends in a space, // or if event processing caused a redraw. @@ -10613,7 +9306,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } n = safe_vgetc(); - } else if (get_tv_number_chk(&argvars[0], &error) == 1) { + } else if (tv_get_number_chk(&argvars[0], &error) == 1) { // getchar(1): only check if char avail n = vpeekc_any(); } else if (error || vpeekc_any() == NUL) { @@ -10624,12 +9317,12 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = safe_vgetc(); } - if (n == K_IGNORE) + if (n == K_IGNORE) { continue; + } break; } - --no_mapping; - --allow_keys; + no_mapping--; vimvars[VV_MOUSE_WIN].vv_nr = 0; vimvars[VV_MOUSE_WINID].vv_nr = 0; @@ -10696,13 +9389,13 @@ static void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "char", 0L, last_csearch()); - dict_add_nr_str(dict, "forward", last_csearch_forward(), NULL); - dict_add_nr_str(dict, "until", last_csearch_until(), NULL); + tv_dict_add_str(dict, S_LEN("char"), last_csearch()); + tv_dict_add_nr(dict, S_LEN("forward"), last_csearch_forward()); + tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until()); } /* @@ -10753,7 +9446,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) | WILD_NO_BEEP; if (argvars[2].v_type != VAR_UNKNOWN) { - filtered = get_tv_number_chk(&argvars[2], NULL); + filtered = (bool)tv_get_number_chk(&argvars[2], NULL); } if (p_wic) { @@ -10765,16 +9458,24 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) options |= WILD_KEEP_ALL; } + if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) { + EMSG(_(e_invarg)); + return; + } + + if (strcmp(tv_get_string(&argvars[1]), "cmdline") == 0) { + set_one_cmd_context(&xpc, tv_get_string(&argvars[0])); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); + goto theend; + } + ExpandInit(&xpc); - xpc.xp_pattern = get_tv_string(&argvars[0]); + xpc.xp_pattern = (char_u *)tv_get_string(&argvars[0]); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); - xpc.xp_context = cmdcomplete_str_to_type(get_tv_string(&argvars[1])); + xpc.xp_context = cmdcomplete_str_to_type( + (char_u *)tv_get_string(&argvars[1])); if (xpc.xp_context == EXPAND_NOTHING) { - if (argvars[1].v_type == VAR_STRING) { - EMSG2(_(e_invarg2), argvars[1].vval.v_string); - } else { - EMSG(_(e_invarg)); - } + EMSG2(_(e_invarg2), argvars[1].vval.v_string); return; } @@ -10784,7 +9485,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (xpc.xp_context == EXPAND_CSCOPE) { - set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope); + set_context_in_cscope_cmd(&xpc, (const char *)xpc.xp_pattern, CMD_cscope); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); } @@ -10793,13 +9494,15 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); } +theend: pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (pat != NULL) { ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], + -1); } } xfree(pat); @@ -10943,20 +9646,21 @@ static void f_getfontname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *perm = NULL; + char *perm = NULL; char_u flags[] = "rwx"; - char_u *filename = get_tv_string(&argvars[0]); + const char *filename = tv_get_string(&argvars[0]); int32_t file_perm = os_getperm(filename); if (file_perm >= 0) { - perm = vim_strsave((char_u *)"---------"); + perm = xstrdup("---------"); for (int i = 0; i < 9; i++) { - if (file_perm & (1 << (8 - i))) + if (file_perm & (1 << (8 - i))) { perm[i] = flags[i % 3]; + } } } rettv->v_type = VAR_STRING; - rettv->vval.v_string = perm; + rettv->vval.v_string = (char_u *)perm; } /* @@ -10964,16 +9668,16 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *fname = (char *)get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); rettv->v_type = VAR_NUMBER; FileInfo file_info; if (os_fileinfo(fname, &file_info)) { uint64_t filesize = os_fileinfo_size(&file_info); - if (os_isdir((char_u *)fname)) + if (os_isdir((const char_u *)fname)) { rettv->vval.v_number = 0; - else { + } else { rettv->vval.v_number = (varnumber_T)filesize; /* non-perfect check for overflow */ @@ -10991,7 +9695,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *fname = (char *)get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); FileInfo file_info; if (os_fileinfo(fname, &file_info)) { @@ -11006,15 +9710,14 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; char_u *type = NULL; char *t; - fname = get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); rettv->v_type = VAR_STRING; FileInfo file_info; - if (os_fileinfo_link((char *)fname, &file_info)) { + if (os_fileinfo_link(fname, &file_info)) { uint64_t mode = file_info.stat.st_mode; #ifdef S_ISREG if (S_ISREG(mode)) @@ -11066,10 +9769,11 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) default: t = "other"; } # else - if (os_isdir(fname)) + if (os_isdir((const char_u *)fname)) { t = "dir"; - else + } else { t = "file"; + } # endif #endif type = vim_strsave((char_u *)t); @@ -11082,17 +9786,16 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; linenr_T end; - int retlist; + bool retlist; - lnum = get_tv_lnum(argvars); + const linenr_T lnum = tv_get_lnum(argvars); if (argvars[1].v_type == VAR_UNKNOWN) { end = 0; - retlist = FALSE; + retlist = false; } else { - end = get_tv_lnum(&argvars[1]); - retlist = TRUE; + end = tv_get_lnum(&argvars[1]); + retlist = true; } get_buffer_lines(curbuf, lnum, end, retlist, rettv); @@ -11102,12 +9805,12 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) { if (what_arg->v_type == VAR_UNKNOWN) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (is_qf || wp != NULL) { (void)get_errorlist(wp, -1, rettv->vval.v_list); } } else { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); if (is_qf || wp != NULL) { if (what_arg->v_type == VAR_DICT) { dict_T *d = what_arg->vval.v_dict; @@ -11137,43 +9840,45 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) matchitem_T *cur = curwin->w_match_head; int i; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); while (cur != NULL) { - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); if (cur->match.regprog == NULL) { // match added with matchaddpos() for (i = 0; i < MAXPOSMATCH; ++i) { llpos_T *llpos; - char buf[6]; + char buf[6]; llpos = &cur->pos.pos[i]; if (llpos->lnum == 0) { break; } - list_T *l = list_alloc(); - list_append_number(l, (varnumber_T)llpos->lnum); + list_T *l = tv_list_alloc(); + tv_list_append_number(l, (varnumber_T)llpos->lnum); if (llpos->col > 0) { - list_append_number(l, (varnumber_T)llpos->col); - list_append_number(l, (varnumber_T)llpos->len); + tv_list_append_number(l, (varnumber_T)llpos->col); + tv_list_append_number(l, (varnumber_T)llpos->len); } - sprintf(buf, "pos%d", i + 1); - dict_add_list(dict, buf, l); + int len = snprintf(buf, sizeof(buf), "pos%d", i + 1); + assert((size_t)len < sizeof(buf)); + tv_dict_add_list(dict, buf, (size_t)len, l); } } else { - dict_add_nr_str(dict, "pattern", 0L, cur->pattern); + tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cur->pattern); } - dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id)); - dict_add_nr_str(dict, "priority", (long)cur->priority, NULL); - dict_add_nr_str(dict, "id", (long)cur->id, NULL); + tv_dict_add_str(dict, S_LEN("group"), + (const char *)syn_id2name(cur->hlg_id)); + tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->priority); + tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->id); if (cur->conceal_char) { - char_u buf[MB_MAXBYTES + 1]; + char buf[MB_MAXBYTES + 1]; - buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL; - dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf); + buf[(*mb_char2bytes)((int)cur->conceal_char, (char_u *)buf)] = NUL; + tv_dict_add_str(dict, S_LEN("conceal"), buf); } - list_append_dict(rettv->vval.v_list, dict); + tv_list_append_dict(rettv->vval.v_list, dict); cur = cur->next; } } @@ -11197,20 +9902,22 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) fp = var2fpos(&argvars[0], true, &fnum); } - list_T *l = rettv_list_alloc(rettv); - list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); - list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0); - list_append_number(l, - (fp != NULL) - ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) - : (varnumber_T)0); - list_append_number(l, - (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); + list_T *l = tv_list_alloc_ret(rettv); + tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); + tv_list_append_number(l, ((fp != NULL) + ? (varnumber_T)fp->lnum + : (varnumber_T)0)); + tv_list_append_number( + l, ((fp != NULL) + ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) + : (varnumber_T)0)); + tv_list_append_number( + l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { update_curswant(); - list_append_number(l, curwin->w_curswant == MAXCOL + tv_list_append_number(l, (curwin->w_curswant == MAXCOL ? (varnumber_T)MAXCOL - : (varnumber_T)curwin->w_curswant + 1); + : (varnumber_T)curwin->w_curswant + 1)); } } @@ -11239,39 +9946,39 @@ static void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "getreg()" function static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *strregname; - int regname; + const char *strregname; int arg2 = false; bool return_list = false; - int error = false; + bool error = false; if (argvars[0].v_type != VAR_UNKNOWN) { - strregname = get_tv_string_chk(&argvars[0]); + strregname = tv_get_string_chk(&argvars[0]); error = strregname == NULL; if (argvars[1].v_type != VAR_UNKNOWN) { - arg2 = get_tv_number_chk(&argvars[1], &error); + arg2 = tv_get_number_chk(&argvars[1], &error); if (!error && argvars[2].v_type != VAR_UNKNOWN) { - return_list = get_tv_number_chk(&argvars[2], &error); + return_list = tv_get_number_chk(&argvars[2], &error); } } } else { - strregname = vimvars[VV_REG].vv_str; + strregname = (const char *)vimvars[VV_REG].vv_str; } if (error) { return; } - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) + int regname = (uint8_t)(strregname == NULL ? '"' : *strregname); + if (regname == 0) { regname = '"'; + } if (return_list) { rettv->v_type = VAR_LIST; rettv->vval.v_list = get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList); if (rettv->vval.v_list == NULL) { - rettv->vval.v_list = list_alloc(); + rettv->vval.v_list = tv_list_alloc(); } rettv->vval.v_list->lv_refcount++; } else { @@ -11285,23 +9992,24 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *strregname; - int regname; + const char *strregname; if (argvars[0].v_type != VAR_UNKNOWN) { - strregname = get_tv_string_chk(&argvars[0]); - if (strregname == NULL) { /* type error; errmsg already given */ + strregname = tv_get_string_chk(&argvars[0]); + if (strregname == NULL) { // Type error; errmsg already given. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; return; } - } else - /* Default to v:register */ - strregname = vimvars[VV_REG].vv_str; + } else { + // Default to v:register. + strregname = (const char *)vimvars[VV_REG].vv_str; + } - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) + int regname = (uint8_t)(strregname == NULL ? '"' : *strregname); + if (regname == 0) { regname = '"'; + } colnr_T reglen = 0; char buf[NUMBUFLEN + 2]; @@ -11316,18 +10024,18 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// as a dictionary. static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); - dict_add_nr_str(dict, "tabnr", tp_idx, NULL); + tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); - list_T *l = list_alloc(); + list_T *const l = tv_list_alloc(); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { - list_append_number(l, (varnumber_T)wp->handle); + tv_list_append_number(l, (varnumber_T)wp->handle); } - dict_add_list(dict, "windows", l); + tv_dict_add_list(dict, S_LEN("windows"), l); // Make a reference to tabpage variables - dict_add_dict(dict, "variables", tp->tp_vars); + tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars); return dict; } @@ -11337,11 +10045,11 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tabpage_T *tparg = NULL; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one tab page - tparg = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); if (tparg == NULL) { return; } @@ -11354,9 +10062,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (tparg != NULL && tp != tparg) { continue; } - dict_T *d = get_tabpage_info(tp, tpnr); + dict_T *const d = get_tabpage_info(tp, tpnr); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (tparg != NULL) { return; @@ -11377,8 +10085,8 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + const char *const varname = tv_get_string_chk(&argvars[1]); + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); if (tp != NULL && varname != NULL) { // Set tp to be our tabpage, temporarily. Also set the window to the // first window in the tabpage, otherwise the window is not valid. @@ -11389,7 +10097,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -11399,7 +10107,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (!done && argvars[2].v_type != VAR_UNKNOWN) { - copy_tv(&argvars[2], rettv); + tv_copy(&argvars[2], rettv); } } @@ -11414,22 +10122,21 @@ static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// Returns information about a window as a dictionary. static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); - dict_add_nr_str(dict, "tabnr", tpnr, NULL); - dict_add_nr_str(dict, "winnr", winnr, NULL); - dict_add_nr_str(dict, "winid", wp->handle, NULL); - dict_add_nr_str(dict, "height", wp->w_height, NULL); - dict_add_nr_str(dict, "width", wp->w_width, NULL); - dict_add_nr_str(dict, "bufnr", wp->w_buffer->b_fnum, NULL); + tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr); + tv_dict_add_nr(dict, S_LEN("winnr"), winnr); + tv_dict_add_nr(dict, S_LEN("winid"), wp->handle); + tv_dict_add_nr(dict, S_LEN("height"), wp->w_height); + tv_dict_add_nr(dict, S_LEN("width"), wp->w_width); + tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); - dict_add_nr_str(dict, "quickfix", bt_quickfix(wp->w_buffer), NULL); - dict_add_nr_str(dict, "loclist", - (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL), - NULL); + tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer)); + tv_dict_add_nr(dict, S_LEN("loclist"), + (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)); // Add a reference to window variables - dict_add_dict(dict, "variables", wp->w_vars); + tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars); return dict; } @@ -11439,7 +10146,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *wparg = NULL; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type != VAR_UNKNOWN) { wparg = win_id2wp(argvars); @@ -11459,9 +10166,9 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } winnr++; - dict_T *d = get_win_info(wp, tabnr, winnr); + dict_T *const d = get_win_info(wp, tabnr, winnr); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (wparg != NULL) { // found information about a specific window @@ -11496,7 +10203,7 @@ find_win_by_nr ( tabpage_T *tp /* NULL for current tab page */ ) { - int nr = get_tv_number_chk(vp, NULL); + int nr = (int)tv_get_number_chk(vp, NULL); if (nr < 0) { return NULL; @@ -11531,7 +10238,7 @@ static win_T *find_tabwin(typval_T *wvp, typval_T *tvp) if (wvp->v_type != VAR_UNKNOWN) { if (tvp->v_type != VAR_UNKNOWN) { - long n = get_tv_number(tvp); + long n = tv_get_number(tvp); if (n >= 0) { tp = find_tabpage(n); } @@ -11571,13 +10278,13 @@ getwinvar ( tabpage_T *oldtabpage = NULL; bool done = false; - if (off == 1) - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { tp = curtab; + } win = find_win_by_nr(&argvars[off], tp); - const char *varname = (const char *)get_tv_string_chk( - &argvars[off + 1]); + const char *varname = tv_get_string_chk(&argvars[off + 1]); rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -11611,7 +10318,7 @@ getwinvar ( v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -11626,7 +10333,7 @@ getwinvar ( if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) { // use the default return value - copy_tv(&argvars[off + 2], rettv); + tv_copy(&argvars[off + 2], rettv); } } @@ -11637,21 +10344,22 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int options = WILD_SILENT|WILD_USE_NL; expand_T xpc; - int error = FALSE; + bool error = false; /* When the optional second argument is non-zero, don't remove matches * for 'wildignore' and don't put matches for 'suffixes' at the end. */ rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[1], &error)) + if (tv_get_number_chk(&argvars[1], &error)) { options |= WILD_KEEP_ALL; + } if (argvars[2].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[2], &error)) { + if (tv_get_number_chk(&argvars[2], &error)) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } if (argvars[3].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[3], &error)) { + && tv_get_number_chk(&argvars[3], &error)) { options |= WILD_ALLLINKS; } } @@ -11661,14 +10369,16 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) xpc.xp_context = EXPAND_FILES; if (p_wic) options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]), - NULL, options, WILD_ALL); - else { - rettv_list_alloc(rettv); - ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); + if (rettv->v_type == VAR_STRING) { + rettv->vval.v_string = ExpandOne( + &xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL); + } else { + tv_list_alloc_ret(rettv); + ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, + WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], + -1); } ExpandCleanup(&xpc); } @@ -11680,7 +10390,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int flags = 0; // Flags for globpath. - int error = false; + bool error = false; // Return a string, or a list if the optional third argument is non-zero. rettv->v_type = VAR_STRING; @@ -11688,36 +10398,36 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { // When the optional second argument is non-zero, don't remove matches // for 'wildignore' and don't put matches for 'suffixes' at the end. - if (get_tv_number_chk(&argvars[2], &error)) { + if (tv_get_number_chk(&argvars[2], &error)) { flags |= WILD_KEEP_ALL; } if (argvars[3].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[3], &error)) { + if (tv_get_number_chk(&argvars[3], &error)) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } if (argvars[4].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[4], &error)) { + && tv_get_number_chk(&argvars[4], &error)) { flags |= WILD_ALLLINKS; } } } - char_u buf1[NUMBUFLEN]; - char_u *file = get_tv_string_buf_chk(&argvars[1], buf1); + char buf1[NUMBUFLEN]; + const char *const file = tv_get_string_buf_chk(&argvars[1], buf1); if (file != NULL && !error) { garray_T ga; ga_init(&ga, (int)sizeof(char_u *), 10); - globpath(get_tv_string(&argvars[0]), file, &ga, flags); + globpath((char_u *)tv_get_string(&argvars[0]), (char_u *)file, &ga, flags); if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); } else { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); for (int i = 0; i < ga.ga_len; i++) { - list_append_string(rettv->vval.v_list, - ((char_u **)(ga.ga_data))[i], -1); + tv_list_append_string(rettv->vval.v_list, + ((const char **)(ga.ga_data))[i], -1); } } @@ -11730,18 +10440,19 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "glob2regpat()" function static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *pat = get_tv_string_chk(&argvars[0]); // NULL on type error + const char *const pat = tv_get_string_chk(&argvars[0]); // NULL on type error rettv->v_type = VAR_STRING; - rettv->vval.v_string = (pat == NULL) - ? NULL - : file_pat_to_reg_pat(pat, NULL, NULL, false); + rettv->vval.v_string = ((pat == NULL) + ? NULL + : file_pat_to_reg_pat((char_u *)pat, NULL, NULL, + false)); } /// "has()" function static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - static char *(has_list[]) = { + static const char *const has_list[] = { #ifdef UNIX "unix", #endif @@ -11852,13 +10563,11 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "winaltkeys", "writebackup", "nvim", - NULL }; bool n = false; - char *name = (char *)get_tv_string(&argvars[0]); - - for (int i = 0; has_list[i] != NULL; i++) { + const char *const name = tv_get_string(&argvars[0]); + for (size_t i = 0; i < ARRAY_SIZE(has_list); i++) { if (STRICMP(name, has_list[i]) == 0) { n = true; break; @@ -11923,8 +10632,9 @@ static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].vval.v_dict == NULL) return; - rettv->vval.v_number = dict_find(argvars[0].vval.v_dict, - get_tv_string(&argvars[1]), -1) != NULL; + rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, + tv_get_string(&argvars[1]), + -1) != NULL; } /// `haslocaldir([{win}[, {tab}]])` function @@ -12033,24 +10743,24 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *name; - char_u *mode; - char_u buf[NUMBUFLEN]; - int abbr = FALSE; - - name = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) - mode = (char_u *)"nvo"; - else { - mode = get_tv_string_buf(&argvars[1], buf); - if (argvars[2].v_type != VAR_UNKNOWN) - abbr = get_tv_number(&argvars[2]); + const char *mode; + const char *const name = tv_get_string(&argvars[0]); + bool abbr = false; + char buf[NUMBUFLEN]; + if (argvars[1].v_type == VAR_UNKNOWN) { + mode = "nvo"; + } else { + mode = tv_get_string_buf(&argvars[1], buf); + if (argvars[2].v_type != VAR_UNKNOWN) { + abbr = tv_get_number(&argvars[2]); + } } - if (map_to_exists(name, mode, abbr)) - rettv->vval.v_number = TRUE; - else - rettv->vval.v_number = FALSE; + if (map_to_exists(name, mode, abbr)) { + rettv->vval.v_number = true; + } else { + rettv->vval.v_number = false; + } } /* @@ -12059,20 +10769,19 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { HistoryType histype; - char_u *str; - char_u buf[NUMBUFLEN]; rettv->vval.v_number = false; if (check_restricted() || check_secure()) { return; } - str = get_tv_string_chk(&argvars[0]); // NULL on type error - histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID; + const char *str = tv_get_string_chk(&argvars[0]); // NULL on type error + histype = str != NULL ? get_histtype(str, strlen(str), false) : HIST_INVALID; if (histype != HIST_INVALID) { - str = get_tv_string_buf(&argvars[1], buf); + char buf[NUMBUFLEN]; + str = tv_get_string_buf(&argvars[1], buf); if (*str != NUL) { init_history(); - add_to_history(histype, str, false, NUL); + add_to_history(histype, (char_u *)str, false, NUL); rettv->vval.v_number = true; return; } @@ -12085,23 +10794,21 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int n; - char_u buf[NUMBUFLEN]; - char_u *str; - - str = get_tv_string_chk(&argvars[0]); // NULL on type error + const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error if (str == NULL) { n = 0; } else if (argvars[1].v_type == VAR_UNKNOWN) { // only one argument: clear entire history - n = clr_history(get_histtype(str, STRLEN(str), false)); + n = clr_history(get_histtype(str, strlen(str), false)); } else if (argvars[1].v_type == VAR_NUMBER) { // index given: remove that entry - n = del_history_idx(get_histtype(str, STRLEN(str), false), - (int) get_tv_number(&argvars[1])); + n = del_history_idx(get_histtype(str, strlen(str), false), + (int)tv_get_number(&argvars[1])); } else { // string given: remove all matching entries - n = del_history_entry(get_histtype(str, STRLEN(str), false), - get_tv_string_buf(&argvars[1], buf)); + char buf[NUMBUFLEN]; + n = del_history_entry(get_histtype(str, strlen(str), false), + (char_u *)tv_get_string_buf(&argvars[1], buf)); } rettv->vval.v_number = n; } @@ -12113,17 +10820,16 @@ static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr) { HistoryType type; int idx; - char_u *str; - str = get_tv_string_chk(&argvars[0]); // NULL on type error + const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error if (str == NULL) { rettv->vval.v_string = NULL; } else { - type = get_histtype(str, STRLEN(str), false); + type = get_histtype(str, strlen(str), false); if (argvars[1].v_type == VAR_UNKNOWN) { idx = get_history_idx(type); } else { - idx = (int)get_tv_number_chk(&argvars[1], NULL); + idx = (int)tv_get_number_chk(&argvars[1], NULL); } // -1 on type error rettv->vval.v_string = vim_strsave(get_history_entry(type, idx)); @@ -12138,9 +10844,9 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int i; - char_u *history = get_tv_string_chk(&argvars[0]); + const char *const history = tv_get_string_chk(&argvars[0]); - i = history == NULL ? HIST_CMD - 1 : get_histtype(history, STRLEN(history), + i = history == NULL ? HIST_CMD - 1 : get_histtype(history, strlen(history), false); if (i != HIST_INVALID) { i = get_history_idx(i); @@ -12155,7 +10861,8 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = syn_name2id(get_tv_string(&argvars[0])); + rettv->vval.v_number = syn_name2id( + (const char_u *)tv_get_string(&argvars[0])); } /* @@ -12163,7 +10870,8 @@ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = highlight_exists(get_tv_string(&argvars[0])); + rettv->vval.v_number = highlight_exists( + (const char_u *)tv_get_string(&argvars[0])); } /* @@ -12183,25 +10891,27 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - char_u *from, *to, *str; vimconv_T vimconv; rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - str = get_tv_string(&argvars[0]); - from = enc_canonize(enc_skip(get_tv_string_buf(&argvars[1], buf1))); - to = enc_canonize(enc_skip(get_tv_string_buf(&argvars[2], buf2))); + const char *const str = tv_get_string(&argvars[0]); + char buf1[NUMBUFLEN]; + char_u *const from = enc_canonize(enc_skip( + (char_u *)tv_get_string_buf(&argvars[1], buf1))); + char buf2[NUMBUFLEN]; + char_u *const to = enc_canonize(enc_skip( + (char_u *)tv_get_string_buf(&argvars[2], buf2))); vimconv.vc_type = CONV_NONE; convert_setup(&vimconv, from, to); - /* If the encodings are equal, no conversion needed. */ - if (vimconv.vc_type == CONV_NONE) - rettv->vval.v_string = vim_strsave(str); - else - rettv->vval.v_string = string_convert(&vimconv, str, NULL); + // If the encodings are equal, no conversion needed. + if (vimconv.vc_type == CONV_NONE) { + rettv->vval.v_string = (char_u *)xstrdup(str); + } else { + rettv->vval.v_string = string_convert(&vimconv, (char_u *)str, NULL); + } convert_setup(&vimconv, NULL, NULL); xfree(from); @@ -12213,13 +10923,12 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { rettv->vval.v_number = get_indent_lnum(lnum); - else + } else { rettv->vval.v_number = -1; + } } /* @@ -12241,16 +10950,18 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (l != NULL) { item = l->lv_first; if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; - /* Start at specified item. Use the cached index that list_find() - * sets, so that a negative number also works. */ - item = list_find(l, get_tv_number_chk(&argvars[2], &error)); + // Start at specified item. Use the cached index that tv_list_find() + // sets, so that a negative number also works. + item = tv_list_find(l, tv_get_number_chk(&argvars[2], &error)); idx = l->lv_idx; - if (argvars[3].v_type != VAR_UNKNOWN) - ic = get_tv_number_chk(&argvars[3], &error); - if (error) + if (argvars[3].v_type != VAR_UNKNOWN) { + ic = tv_get_number_chk(&argvars[3], &error); + } + if (error) { item = NULL; + } } for (; item != NULL; item = item->li_next, ++idx) @@ -12272,12 +10983,8 @@ static int inputsecret_flag = 0; */ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) { - char_u *prompt = get_tv_string_chk(&argvars[0]); - char_u *p = NULL; - int c; - char_u buf[NUMBUFLEN]; + const char *prompt = tv_get_string_chk(&argvars[0]); int cmd_silent_save = cmd_silent; - char_u *defstr = (char_u *)""; int xp_type = EXPAND_NOTHING; char_u *xp_arg = NULL; @@ -12286,46 +10993,46 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) cmd_silent = FALSE; /* Want to see the prompt. */ if (prompt != NULL) { - /* Only the part of the message after the last NL is considered as - * prompt for the command line */ - p = vim_strrchr(prompt, '\n'); - if (p == NULL) + // Only the part of the message after the last NL is considered as + // prompt for the command line. + const char *p = strrchr(prompt, '\n'); + if (p == NULL) { p = prompt; - else { - ++p; - c = *p; - *p = NUL; + } else { + p++; msg_start(); msg_clr_eos(); - msg_puts_attr((const char *)prompt, echo_attr); + msg_puts_attr_len(prompt, p - prompt, echo_attr); msg_didout = false; msg_starthere(); - *p = c; } cmdline_row = msg_row; + const char *defstr = ""; + char buf[NUMBUFLEN]; if (argvars[1].v_type != VAR_UNKNOWN) { - defstr = get_tv_string_buf_chk(&argvars[1], buf); - if (defstr != NULL) + defstr = tv_get_string_buf_chk(&argvars[1], buf); + if (defstr != NULL) { stuffReadbuffSpec(defstr); + } if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { - char_u *xp_name; - int xp_namelen; - uint32_t argt; - - /* input() with a third argument: completion */ + char buf2[NUMBUFLEN]; + // input() with a third argument: completion rettv->vval.v_string = NULL; - xp_name = get_tv_string_buf_chk(&argvars[2], buf); - if (xp_name == NULL) + const char *const xp_name = tv_get_string_buf_chk(&argvars[2], buf2); + if (xp_name == NULL) { return; + } - xp_namelen = (int)STRLEN(xp_name); + const int xp_namelen = (int)strlen(xp_name); - if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, - &xp_arg) == FAIL) + uint32_t argt; + if (parse_compl_arg((char_u *)xp_name, xp_namelen, &xp_type, &argt, + &xp_arg) == FAIL) { return; + } } } @@ -12333,15 +11040,17 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) int save_ex_normal_busy = ex_normal_busy; ex_normal_busy = 0; rettv->vval.v_string = - getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr, - xp_type, xp_arg); + getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr, + xp_type, xp_arg); ex_normal_busy = save_ex_normal_busy; } if (inputdialog && rettv->vval.v_string == NULL && argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) - rettv->vval.v_string = vim_strsave(get_tv_string_buf( - &argvars[2], buf)); + && argvars[2].v_type != VAR_UNKNOWN) { + char buf[NUMBUFLEN]; + rettv->vval.v_string = (char_u *)xstrdup(tv_get_string_buf( + &argvars[2], buf)); + } xfree(xp_arg); @@ -12390,7 +11099,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) msg_clr_eos(); for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) { - msg_puts((const char *)get_tv_string(&li->li_tv)); + msg_puts(tv_get_string(&li->li_tv)); msg_putchar('\n'); } @@ -12448,10 +11157,8 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long before = 0; - listitem_T *item; - list_T *l; - int error = false; + list_T *l; + bool error = false; const char *const arg_errmsg = _("insert() argument"); const size_t arg_errmsg_len = strlen(arg_errmsg); @@ -12459,26 +11166,26 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "insert()"); } else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { + long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - before = get_tv_number_chk(&argvars[2], &error); + before = tv_get_number_chk(&argvars[2], &error); } if (error) { // type error; errmsg already given return; } - if (before == l->lv_len) - item = NULL; - else { - item = list_find(l, before); + listitem_T *item = NULL; + if (before != l->lv_len) { + item = tv_list_find(l, before); if (item == NULL) { EMSGN(_(e_listidx), before); l = NULL; } } if (l != NULL) { - list_insert_tv(l, &argvars[1], item); - copy_tv(&argvars[0], rettv); + tv_list_insert_tv(l, &argvars[1], item); + tv_copy(&argvars[0], rettv); } } } @@ -12488,7 +11195,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = ~get_tv_number_chk(&argvars[0], NULL); + rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL); } /* @@ -12496,7 +11203,7 @@ static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = os_isdir(get_tv_string(&argvars[0])); + rettv->vval.v_number = os_isdir((const char_u *)tv_get_string(&argvars[0])); } /* @@ -12505,18 +11212,20 @@ static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) { lval_T lv; - char_u *end; dictitem_T *di; rettv->vval.v_number = -1; - end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, false, false, - GLV_NO_AUTOLOAD|GLV_READ_ONLY, FNE_CHECK_START); + const char_u *const end = get_lval((char_u *)tv_get_string(&argvars[0]), + NULL, + &lv, false, false, + GLV_NO_AUTOLOAD|GLV_READ_ONLY, + FNE_CHECK_START); if (end != NULL && lv.ll_name != NULL) { - if (*end != NUL) + if (*end != NUL) { EMSG(_(e_trailing)); - else { + } else { if (lv.ll_tv == NULL) { - di = find_var((const char *)lv.ll_name, STRLEN(lv.ll_name), NULL, true); + di = find_var((const char *)lv.ll_name, lv.ll_name_len, NULL, true); if (di != NULL) { // Consider a variable locked when: // 1. the variable itself is locked @@ -12543,68 +11252,61 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -/* - * Turn a dict into a list: - * "what" == 0: list of keys - * "what" == 1: list of values - * "what" == 2: list of items - */ -static void dict_list(typval_T *argvars, typval_T *rettv, int what) -{ - list_T *l2; - dictitem_T *di; - hashitem_T *hi; - listitem_T *li; - listitem_T *li2; - dict_T *d; - int todo; - - if (argvars[0].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); +/// Turn a dictionary into a list +/// +/// @param[in] tv Dictionary to convert. Is checked for actually being +/// a dictionary, will give an error if not. +/// @param[out] rettv Location where result will be saved. +/// @param[in] what What to save in rettv. +static void dict_list(typval_T *const tv, typval_T *const rettv, + const DictListType what) +{ + if (tv->v_type != VAR_DICT) { + emsgf(_(e_dictreq)); return; } - if ((d = argvars[0].vval.v_dict) == NULL) + if (tv->vval.v_dict == NULL) { return; + } - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - di = HI2DI(hi); - - li = listitem_alloc(); - list_append(rettv->vval.v_list, li); + TV_DICT_ITER(tv->vval.v_dict, di, { + listitem_T *const li = tv_list_item_alloc(); + tv_list_append(rettv->vval.v_list, li); - if (what == 0) { - /* keys() */ + switch (what) { + case kDictListKeys: { li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; + li->li_tv.v_lock = VAR_UNLOCKED; li->li_tv.vval.v_string = vim_strsave(di->di_key); - } else if (what == 1) { - /* values() */ - copy_tv(&di->di_tv, &li->li_tv); - } else { - /* items() */ - l2 = list_alloc(); + break; + } + case kDictListValues: { + tv_copy(&di->di_tv, &li->li_tv); + break; + } + case kDictListItems: { + // items() + list_T *const sub_l = tv_list_alloc(); li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = l2; - ++l2->lv_refcount; - - li2 = listitem_alloc(); - list_append(l2, li2); - li2->li_tv.v_type = VAR_STRING; - li2->li_tv.v_lock = 0; - li2->li_tv.vval.v_string = vim_strsave(di->di_key); - - li2 = listitem_alloc(); - list_append(l2, li2); - copy_tv(&di->di_tv, &li2->li_tv); + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_list = sub_l; + sub_l->lv_refcount++; + + listitem_T *sub_li = tv_list_item_alloc(); + tv_list_append(sub_l, sub_li); + sub_li->li_tv.v_type = VAR_STRING; + sub_li->li_tv.v_lock = VAR_UNLOCKED; + sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); + + sub_li = tv_list_item_alloc(); + tv_list_append(sub_l, sub_li); + tv_copy(&di->di_tv, &sub_li->li_tv); + break; } } - } + }); } /// "id()" function @@ -12745,8 +11447,8 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - ssize_t input_len; - char *input = (char *) save_tv_as_string(&argvars[1], &input_len, false); + ptrdiff_t input_len = 0; + char *input = save_tv_as_string(&argvars[1], &input_len, false); if (!input) { // Either the error has been handled by save_tv_as_string(), or there is no // input to send. @@ -12791,10 +11493,10 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; } -static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable) +static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) { if (cmd_tv->v_type == VAR_STRING) { - char *cmd_str = (char *)get_tv_string(cmd_tv); + const char *cmd_str = tv_get_string(cmd_tv); if (cmd) { *cmd = cmd_str; } @@ -12815,8 +11517,8 @@ static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable) assert(argl->lv_first); - const char_u *exe = get_tv_string_chk(&argl->lv_first->li_tv); - if (!exe || !os_can_exe(exe, NULL, true)) { + const char *exe = tv_get_string_chk(&argl->lv_first->li_tv); + if (!exe || !os_can_exe((const char_u *)exe, NULL, true)) { if (exe && executable) { *executable = false; } @@ -12824,16 +11526,16 @@ static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable) } if (cmd) { - *cmd = (char *)exe; + *cmd = exe; } // Build the argument vector int i = 0; char **argv = xcalloc(argc + 1, sizeof(char *)); for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) { - char *a = (char *)get_tv_string_chk(&arg->li_tv); + const char *a = tv_get_string_chk(&arg->li_tv); if (!a) { - // Did emsg in get_tv_string; just deallocate argv. + // Did emsg in tv_get_string_chk; just deallocate argv. shell_free_argv(argv); return NULL; } @@ -12869,23 +11571,26 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *job_opts = NULL; - bool detach = false, rpc = false, pty = false; - Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, - on_exit = CALLBACK_NONE; + bool detach = false; + bool rpc = false; + bool pty = false; + Callback on_stdout = CALLBACK_NONE; + Callback on_stderr = CALLBACK_NONE; + Callback on_exit = CALLBACK_NONE; char *cwd = NULL; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; - detach = get_dict_number(job_opts, "detach") != 0; - rpc = get_dict_number(job_opts, "rpc") != 0; - pty = get_dict_number(job_opts, "pty") != 0; + detach = tv_dict_get_number(job_opts, "detach") != 0; + rpc = tv_dict_get_number(job_opts, "rpc") != 0; + pty = tv_dict_get_number(job_opts, "pty") != 0; if (pty && rpc) { EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set"); shell_free_argv(argv); return; } - char *new_cwd = (char *)get_dict_string(job_opts, "cwd", false); + char *new_cwd = tv_dict_get_string(job_opts, "cwd", false); if (new_cwd && strlen(new_cwd) > 0) { cwd = new_cwd; // The new cwd must be a directory. @@ -12907,15 +11612,15 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) Process *proc = (Process *)&data->proc; if (pty) { - uint16_t width = get_dict_number(job_opts, "width"); + uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width"); if (width > 0) { data->proc.pty.width = width; } - uint16_t height = get_dict_number(job_opts, "height"); + uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height"); if (height > 0) { data->proc.pty.height = height; } - char *term = (char *)get_dict_string(job_opts, "TERM", true); + char *term = tv_dict_get_string(job_opts, "TERM", true); if (term) { data->proc.pty.term_name = term; } @@ -12975,7 +11680,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } list_T *args = argvars[0].vval.v_list; - list_T *rv = list_alloc(); + list_T *rv = tv_list_alloc(); ui_busy_start(); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); @@ -12986,11 +11691,11 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) TerminalJobData *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER || !(data = find_job(arg->li_tv.vval.v_number))) { - list_append_number(rv, -3); + tv_list_append_number(rv, -3); } else { // append the list item and set the status pointer so we'll collect the // status code when the job exits - list_append_number(rv, -1); + tv_list_append_number(rv, -1); data->status_ptr = &rv->lv_last->li_tv.vval.v_number; // Process any pending events for the job because we'll temporarily // replace the parent queue @@ -13071,50 +11776,49 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - garray_T ga; - char_u *sep; - if (argvars[0].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } - if (argvars[0].vval.v_list == NULL) + if (argvars[0].vval.v_list == NULL) { return; - if (argvars[1].v_type == VAR_UNKNOWN) - sep = (char_u *)" "; - else - sep = get_tv_string_chk(&argvars[1]); + } + const char *const sep = (argvars[1].v_type == VAR_UNKNOWN + ? " " + : tv_get_string_chk(&argvars[1])); rettv->v_type = VAR_STRING; if (sep != NULL) { + garray_T ga; ga_init(&ga, (int)sizeof(char), 80); - list_join(&ga, argvars[0].vval.v_list, (char *) sep); + tv_list_join(&ga, argvars[0].vval.v_list, sep); ga_append(&ga, NUL); rettv->vval.v_string = (char_u *)ga.ga_data; - } else + } else { rettv->vval.v_string = NULL; + } } /// json_decode() function static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char numbuf[NUMBUFLEN]; - char *s = NULL; + const char *s = NULL; char *tofree = NULL; size_t len; if (argvars[0].v_type == VAR_LIST) { - if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &s)) { + if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &tofree)) { EMSG(_("E474: Failed to convert list to string")); return; } - tofree = s; + s = tofree; if (s == NULL) { assert(len == 0); s = ""; } } else { - s = (char *) get_tv_string_buf_chk(&argvars[0], (char_u *) numbuf); + s = tv_get_string_buf_chk(&argvars[0], numbuf); if (s) { len = strlen(s); } else { @@ -13167,24 +11871,28 @@ static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) { switch (argvars[0].v_type) { - case VAR_STRING: - case VAR_NUMBER: - rettv->vval.v_number = (varnumber_T)STRLEN( - get_tv_string(&argvars[0])); - break; - case VAR_LIST: - rettv->vval.v_number = list_len(argvars[0].vval.v_list); - break; - case VAR_DICT: - rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); - break; - case VAR_UNKNOWN: - case VAR_SPECIAL: - case VAR_FLOAT: - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E701: Invalid type for len()")); - break; + case VAR_STRING: + case VAR_NUMBER: { + rettv->vval.v_number = (varnumber_T)strlen( + tv_get_string(&argvars[0])); + break; + } + case VAR_LIST: { + rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); + break; + } + case VAR_DICT: { + rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); + break; + } + case VAR_UNKNOWN: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E701: Invalid type for len()")); + break; + } } } @@ -13269,15 +11977,15 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) { rettv->vval.v_number = -1; - else + } else { rettv->vval.v_number = ml_find_line_or_offset(curbuf, lnum, NULL); - if (rettv->vval.v_number >= 0) - ++rettv->vval.v_number; + } + if (rettv->vval.v_number >= 0) { + rettv->vval.v_number++; + } } /* @@ -13285,17 +11993,15 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); + const pos_T pos = curwin->w_cursor; + const linenr_T lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = lnum; rettv->vval.v_number = get_lisp_indent(); curwin->w_cursor = pos; - } else + } else { rettv->vval.v_number = -1; + } } /* @@ -13309,38 +12015,41 @@ static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) { - char_u *keys; - char_u *which; - char_u buf[NUMBUFLEN]; - char_u *keys_buf = NULL; - char_u *rhs; + char_u *keys_buf = NULL; + char_u *rhs; int mode; int abbr = FALSE; int get_dict = FALSE; mapblock_T *mp; int buffer_local; - /* return empty string for failure */ + // Return empty string for failure. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - keys = get_tv_string(&argvars[0]); - if (*keys == NUL) + char_u *keys = (char_u *)tv_get_string(&argvars[0]); + if (*keys == NUL) { return; + } + char buf[NUMBUFLEN]; + const char *which; if (argvars[1].v_type != VAR_UNKNOWN) { - which = get_tv_string_buf_chk(&argvars[1], buf); + which = tv_get_string_buf_chk(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { - abbr = get_tv_number(&argvars[2]); - if (argvars[3].v_type != VAR_UNKNOWN) - get_dict = get_tv_number(&argvars[3]); + abbr = tv_get_number(&argvars[2]); + if (argvars[3].v_type != VAR_UNKNOWN) { + get_dict = tv_get_number(&argvars[3]); + } } - } else - which = (char_u *)""; - if (which == NULL) + } else { + which = ""; + } + if (which == NULL) { return; + } - mode = get_map_mode(&which, 0); + mode = get_map_mode((char_u **)&which, 0); keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false, CPO_TO_CPO_FLAGS); @@ -13353,22 +12062,22 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) rettv->vval.v_string = str2special_save(rhs, FALSE); } else { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); if (rhs != NULL) { // Return a dictionary. char_u *lhs = str2special_save(mp->m_keys, true); char *const mapmode = map_mode_to_chars(mp->m_mode); dict_T *dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "lhs", 0L, lhs); - dict_add_nr_str(dict, "rhs", 0L, mp->m_orig_str); - dict_add_nr_str(dict, "noremap", mp->m_noremap ? 1L : 0L, NULL); - dict_add_nr_str(dict, "expr", mp->m_expr ? 1L : 0L, NULL); - dict_add_nr_str(dict, "silent", mp->m_silent ? 1L : 0L, NULL); - dict_add_nr_str(dict, "sid", (long)mp->m_script_ID, NULL); - dict_add_nr_str(dict, "buffer", (long)buffer_local, NULL); - dict_add_nr_str(dict, "nowait", mp->m_nowait ? 1L : 0L, NULL); - dict_add_nr_str(dict, "mode", 0L, (char_u *)mapmode); + tv_dict_add_str(dict, S_LEN("lhs"), (const char *)lhs); + tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); + tv_dict_add_nr(dict, S_LEN("noremap"), mp->m_noremap ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID); + tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_local); + tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); + tv_dict_add_str(dict, S_LEN("mode"), mapmode); xfree(lhs); xfree(mapmode); @@ -13406,9 +12115,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) char_u *str = NULL; long len = 0; char_u *expr = NULL; - char_u *pat; regmatch_T regmatch; - char_u patbuf[NUMBUFLEN]; char_u *save_cpo; long start = 0; long nth = 1; @@ -13427,12 +12134,12 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (type == 3 || type == 4) { // type 3: return empty list when there are no matches. // type 4: return ["", -1, -1, -1] - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (type == 4) { - list_append_string(rettv->vval.v_list, (char_u *)"", 0); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); + tv_list_append_string(rettv->vval.v_list, "", 0); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); } } else if (type == 2) { rettv->v_type = VAR_STRING; @@ -13444,25 +12151,29 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) goto theend; li = l->lv_first; } else { - expr = str = get_tv_string(&argvars[0]); + expr = str = (char_u *)tv_get_string(&argvars[0]); len = (long)STRLEN(str); } - pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) + char patbuf[NUMBUFLEN]; + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) { goto theend; + } if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; - start = get_tv_number_chk(&argvars[2], &error); - if (error) + start = tv_get_number_chk(&argvars[2], &error); + if (error) { goto theend; + } if (l != NULL) { - li = list_find(l, start); - if (li == NULL) + li = tv_list_find(l, start); + if (li == NULL) { goto theend; - idx = l->lv_idx; /* use the cached index */ + } + idx = l->lv_idx; // Use the cached index. } else { if (start < 0) start = 0; @@ -13479,13 +12190,15 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } } - if (argvars[3].v_type != VAR_UNKNOWN) - nth = get_tv_number_chk(&argvars[3], &error); - if (error) + if (argvars[3].v_type != VAR_UNKNOWN) { + nth = tv_get_number_chk(&argvars[3], &error); + } + if (error) { goto theend; + } } - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = p_ic; @@ -13544,29 +12257,32 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) /* return list with matched string and submatches */ for (i = 0; i < NSUBEXP; ++i) { if (regmatch.endp[i] == NULL) { - list_append_string(rettv->vval.v_list, (char_u *)"", 0); + tv_list_append_string(rettv->vval.v_list, NULL, 0); } else { - list_append_string(rettv->vval.v_list, - regmatch.startp[i], - (int)(regmatch.endp[i] - regmatch.startp[i])); + tv_list_append_string(rettv->vval.v_list, + (const char *)regmatch.startp[i], + (regmatch.endp[i] - regmatch.startp[i])); } } } else if (type == 2) { - /* return matched string */ - if (l != NULL) - copy_tv(&li->li_tv, rettv); - else - rettv->vval.v_string = vim_strnsave(regmatch.startp[0], - (int)(regmatch.endp[0] - regmatch.startp[0])); - } else if (l != NULL) + // Return matched string. + if (l != NULL) { + tv_copy(&li->li_tv, rettv); + } else { + rettv->vval.v_string = (char_u *)xmemdupz( + (const char *)regmatch.startp[0], + (size_t)(regmatch.endp[0] - regmatch.startp[0])); + } + } else if (l != NULL) { rettv->vval.v_number = idx; - else { - if (type != 0) + } else { + if (type != 0) { rettv->vval.v_number = (varnumber_T)(regmatch.startp[0] - str); - else + } else { rettv->vval.v_number = (varnumber_T)(regmatch.endp[0] - str); + } rettv->vval.v_number += (varnumber_T)(str - expr); } } @@ -13575,7 +12291,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (type == 4 && l == NULL) { // matchstrpos() without a list: drop the second item - listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first->li_next); + tv_list_item_remove(rettv->vval.v_list, + rettv->vval.v_list->lv_first->li_next); } theend: @@ -13596,36 +12313,38 @@ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *grp = get_tv_string_buf_chk(&argvars[0], buf); /* group */ - char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ - int prio = 10; /* default priority */ + char grpbuf[NUMBUFLEN]; + char patbuf[NUMBUFLEN]; + const char *const grp = tv_get_string_buf_chk(&argvars[0], grpbuf); + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + int prio = 10; int id = -1; - int error = false; - char_u *conceal_char = NULL; + bool error = false; + const char *conceal_char = NULL; rettv->vval.v_number = -1; - if (grp == NULL || pat == NULL) + if (grp == NULL || pat == NULL) { return; + } if (argvars[2].v_type != VAR_UNKNOWN) { - prio = get_tv_number_chk(&argvars[2], &error); + prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - id = get_tv_number_chk(&argvars[3], &error); + id = tv_get_number_chk(&argvars[3], &error); if (argvars[4].v_type != VAR_UNKNOWN) { if (argvars[4].v_type != VAR_DICT) { EMSG(_(e_dictreq)); return; } - if (dict_find(argvars[4].vval.v_dict, - (char_u *)"conceal", -1) != NULL) { - conceal_char = get_dict_string(argvars[4].vval.v_dict, - "conceal", false); + dictitem_T *di; + if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) + != NULL) { + conceal_char = tv_get_string(&di->di_tv); } } } } - if (error == true) { + if (error) { return; } if (id >= 1 && id <= 3) { @@ -13640,10 +12359,9 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; - - char_u buf[NUMBUFLEN]; - char_u *group; - group = get_tv_string_buf_chk(&argvars[0], buf); + + char buf[NUMBUFLEN]; + const char *const group = tv_get_string_buf_chk(&argvars[0], buf); if (group == NULL) { return; } @@ -13659,24 +12377,24 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - int error = false; + bool error = false; int prio = 10; int id = -1; - char_u *conceal_char = NULL; + const char *conceal_char = NULL; if (argvars[2].v_type != VAR_UNKNOWN) { - prio = get_tv_number_chk(&argvars[2], &error); + prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - id = get_tv_number_chk(&argvars[3], &error); + id = tv_get_number_chk(&argvars[3], &error); if (argvars[4].v_type != VAR_UNKNOWN) { if (argvars[4].v_type != VAR_DICT) { EMSG(_(e_dictreq)); return; } - if (dict_find(argvars[4].vval.v_dict, - (char_u *)"conceal", -1) != NULL) { - conceal_char = get_dict_string(argvars[4].vval.v_dict, - "conceal", false); + dictitem_T *di; + if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) + != NULL) { + conceal_char = tv_get_string(&di->di_tv); } } } @@ -13700,19 +12418,20 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); if (id >= 1 && id <= 3) { matchitem_T *m; if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) { - list_append_string(rettv->vval.v_list, syn_id2name(m->hlg_id), -1); - list_append_string(rettv->vval.v_list, m->pattern, -1); + tv_list_append_string(rettv->vval.v_list, + (const char *)syn_id2name(m->hlg_id), -1); + tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); } else { - list_append_string(rettv->vval.v_list, NULL, -1); - list_append_string(rettv->vval.v_list, NULL, -1); + tv_list_append_string(rettv->vval.v_list, NULL, 0); + tv_list_append_string(rettv->vval.v_list, NULL, 0); } } } @@ -13723,7 +12442,7 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = match_delete(curwin, - (int)get_tv_number(&argvars[0]), TRUE); + (int)tv_get_number(&argvars[0]), true); } /* @@ -13756,54 +12475,53 @@ static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) find_some_match(argvars, rettv, 4); } -static void max_min(typval_T *argvars, typval_T *rettv, int domax) +/// Get maximal/minimal number value in a list or dictionary +/// +/// @param[in] tv List or dictionary to work with. If it contains something +/// that is not an integer number (or cannot be coerced to +/// it) error is given. +/// @param[out] rettv Location where result will be saved. Only assigns +/// vval.v_number, type is not touched. Returns zero for +/// empty lists/dictionaries. +/// @param[in] domax Determines whether maximal or minimal value is desired. +static void max_min(const typval_T *const tv, typval_T *const rettv, + const bool domax) + FUNC_ATTR_NONNULL_ALL { - long n = 0; - long i; - int error = FALSE; - - if (argvars[0].v_type == VAR_LIST) { - list_T *l; - listitem_T *li; + varnumber_T n = 0; + bool error = false; - l = argvars[0].vval.v_list; - if (l != NULL) { - li = l->lv_first; - if (li != NULL) { - n = get_tv_number_chk(&li->li_tv, &error); - for (;; ) { - li = li->li_next; - if (li == NULL) - break; - i = get_tv_number_chk(&li->li_tv, &error); - if (domax ? i > n : i < n) - n = i; + if (tv->v_type == VAR_LIST) { + const list_T *const l = tv->vval.v_list; + if (tv_list_len(l) != 0) { + n = tv_get_number_chk(&l->lv_first->li_tv, &error); + for (const listitem_T *li = l->lv_first->li_next; li != NULL && !error; + li = li->li_next) { + const varnumber_T i = tv_get_number_chk(&li->li_tv, &error); + if (domax ? i > n : i < n) { + n = i; } } } - } else if (argvars[0].v_type == VAR_DICT) { - dict_T *d; - int first = TRUE; - hashitem_T *hi; - int todo; - - d = argvars[0].vval.v_dict; - if (d != NULL) { - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - i = get_tv_number_chk(&HI2DI(hi)->di_tv, &error); - if (first) { - n = i; - first = FALSE; - } else if (domax ? i > n : i < n) - n = i; + } else if (tv->v_type == VAR_DICT) { + if (tv->vval.v_dict != NULL) { + bool first = true; + TV_DICT_ITER(tv->vval.v_dict, di, { + const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); + if (error) { + break; } - } + if (first) { + n = i; + first = true; + } else if (domax ? i > n : i < n) { + n = i; + } + }); } - } else - EMSG(_(e_listdictarg)); + } else { + EMSG2(_(e_listdictarg), domax ? "max()" : "min()"); + } rettv->vval.v_number = error ? 0 : n; } @@ -13828,28 +12546,29 @@ static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *dir; - char_u buf[NUMBUFLEN]; int prot = 0755; rettv->vval.v_number = FAIL; if (check_restricted() || check_secure()) return; - dir = get_tv_string_buf(&argvars[0], buf); - if (*dir == NUL) + char buf[NUMBUFLEN]; + const char *const dir = tv_get_string_buf(&argvars[0], buf); + if (*dir == NUL) { rettv->vval.v_number = FAIL; - else { - if (*path_tail(dir) == NUL) - /* remove trailing slashes */ - *path_tail_with_sep(dir) = NUL; + } else { + if (*path_tail((char_u *)dir) == NUL) { + // Remove trailing slashes. + *path_tail_with_sep((char_u *)dir) = NUL; + } if (argvars[1].v_type != VAR_UNKNOWN) { - if (argvars[2].v_type != VAR_UNKNOWN) - prot = get_tv_number_chk(&argvars[2], NULL); - if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) { + if (argvars[2].v_type != VAR_UNKNOWN) { + prot = tv_get_number_chk(&argvars[2], NULL); + } + if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) { char *failed_dir; - int ret = os_mkdir_recurse((char *) dir, prot, &failed_dir); + int ret = os_mkdir_recurse(dir, prot, &failed_dir); if (ret != 0) { EMSG3(_(e_mkdir), failed_dir, os_strerror(ret)); xfree(failed_dir); @@ -13929,7 +12648,7 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackdump()"); return; } - list_T *ret_list = rettv_list_alloc(rettv); + list_T *ret_list = tv_list_alloc_ret(rettv); const list_T *list = argvars[0].vval.v_list; if (list == NULL) { return; @@ -13957,7 +12676,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackparse()"); return; } - list_T *ret_list = rettv_list_alloc(rettv); + list_T *ret_list = tv_list_alloc_ret(rettv); const list_T *list = argvars[0].vval.v_list; if (list == NULL || list->lv_first == NULL) { return; @@ -14002,9 +12721,9 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) goto f_msgpackparse_exit; } if (result == MSGPACK_UNPACK_SUCCESS) { - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); li->li_tv.v_type = VAR_UNKNOWN; - list_append(ret_list, li); + tv_list_append(ret_list, li); if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) { EMSG2(_(e_invarg2), "Failed to convert msgpack string"); goto f_msgpackparse_exit; @@ -14035,13 +12754,14 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum; - for (lnum = get_tv_lnum(argvars);; ++lnum) { + for (lnum = tv_get_lnum(argvars);; lnum++) { if (lnum < 0 || lnum > curbuf->b_ml.ml_line_count) { lnum = 0; break; } - if (*skipwhite(ml_get(lnum)) != NUL) + if (*skipwhite(ml_get(lnum)) != NUL) { break; + } } rettv->vval.v_number = lnum; } @@ -14056,14 +12776,16 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (has_mbyte) { int utf8 = 0; - if (argvars[1].v_type != VAR_UNKNOWN) - utf8 = get_tv_number_chk(&argvars[1], NULL); - if (utf8) - buf[(*utf_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; - else - buf[(*mb_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; + if (argvars[1].v_type != VAR_UNKNOWN) { + utf8 = tv_get_number_chk(&argvars[1], NULL); + } + if (utf8) { + buf[(*utf_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + } else { + buf[(*mb_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + } } else { - buf[0] = (char_u)get_tv_number(&argvars[0]); + buf[0] = (char_u)tv_get_number(&argvars[0]); buf[1] = NUL; } rettv->v_type = VAR_STRING; @@ -14075,8 +12797,8 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - | get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + | tv_get_number_chk(&argvars[1], NULL); } /* @@ -14085,11 +12807,11 @@ static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = get_tv_string_chk(&argvars[0]); - if (!rettv->vval.v_string) { + const char *const s = tv_get_string_chk(&argvars[0]); + if (!s) { return; } - rettv->vval.v_string = shorten_dir(vim_strsave(rettv->vval.v_string)); + rettv->vval.v_string = shorten_dir((char_u *)xstrdup(s)); } /* @@ -14097,14 +12819,15 @@ static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = pow(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* @@ -14112,14 +12835,14 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) + linenr_T lnum = tv_get_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { lnum = 0; - else - while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) - --lnum; + } else { + while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) { + lnum--; + } + } rettv->vval.v_number = lnum; } @@ -14131,14 +12854,13 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; { - char_u buf[NUMBUFLEN]; int len; int saved_did_emsg = did_emsg; - char *fmt; - /* Get the required length, allocate the buffer and do it for real. */ - did_emsg = FALSE; - fmt = (char *)get_tv_string_buf(&argvars[0], buf); + // Get the required length, allocate the buffer and do it for real. + did_emsg = false; + char buf[NUMBUFLEN]; + const char *fmt = tv_get_string_buf(&argvars[0], buf); len = vim_vsnprintf(NULL, 0, fmt, dummy_ap, argvars + 1); if (!did_emsg) { char *s = xmalloc(len + 1); @@ -14179,32 +12901,34 @@ static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long start; - long end; - long stride = 1; + varnumber_T start; + varnumber_T end; + varnumber_T stride = 1; long i; - int error = FALSE; + bool error = false; - start = get_tv_number_chk(&argvars[0], &error); + start = tv_get_number_chk(&argvars[0], &error); if (argvars[1].v_type == VAR_UNKNOWN) { end = start - 1; start = 0; } else { - end = get_tv_number_chk(&argvars[1], &error); - if (argvars[2].v_type != VAR_UNKNOWN) - stride = get_tv_number_chk(&argvars[2], &error); + end = tv_get_number_chk(&argvars[1], &error); + if (argvars[2].v_type != VAR_UNKNOWN) { + stride = tv_get_number_chk(&argvars[2], &error); + } } - if (error) - return; /* type error; errmsg already given */ - if (stride == 0) - EMSG(_("E726: Stride is zero")); - else if (stride > 0 ? end + 1 < start : end - 1 > start) - EMSG(_("E727: Start past end")); - else { - rettv_list_alloc(rettv); + if (error) { + return; // Type error; errmsg already given. + } + if (stride == 0) { + emsgf(_("E726: Stride is zero")); + } else if (stride > 0 ? end + 1 < start : end - 1 > start) { + emsgf(_("E727: Start past end")); + } else { + tv_list_alloc_ret(rettv); for (i = start; stride > 0 ? i <= end : i >= end; i += stride) { - list_append_number(rettv->vval.v_list, (varnumber_T)i); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)i); } } } @@ -14214,8 +12938,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int binary = FALSE; - char_u *fname; + bool binary = false; FILE *fd; char_u buf[(IOSIZE/256)*256]; /* rounded to avoid odd + 1 */ int io_size = sizeof(buf); @@ -14229,19 +12952,21 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *start; /* start of current line */ if (argvars[1].v_type != VAR_UNKNOWN) { - if (STRCMP(get_tv_string(&argvars[1]), "b") == 0) - binary = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) - maxline = get_tv_number(&argvars[2]); + if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { + binary = true; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + maxline = tv_get_number(&argvars[2]); + } } - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); - /* Always open the file in binary mode, library functions have a mind of - * their own about CR-LF conversion. */ - fname = get_tv_string(&argvars[0]); - if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL) { - EMSG2(_(e_notopen), *fname == NUL ? (char_u *)_("<empty>") : fname); + // Always open the file in binary mode, library functions have a mind of + // their own about CR-LF conversion. + const char *const fname = tv_get_string(&argvars[0]); + if (*fname == NUL || (fd = mch_fopen(fname, READBIN)) == NULL) { + EMSG2(_(e_notopen), *fname == NUL ? _("<empty>") : fname); return; } @@ -14284,11 +13009,11 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) prevlen = prevsize = 0; } - li = listitem_alloc(); + li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; li->li_tv.vval.v_string = s; - list_append(rettv->vval.v_list, li); + tv_list_append(rettv->vval.v_list, li); start = p + 1; /* step over newline */ if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) @@ -14363,8 +13088,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ if (maxline < 0) while (cnt > -maxline) { - listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); - --cnt; + tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); + cnt--; } xfree(prev); @@ -14386,9 +13111,9 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL return FAIL; } - int error = false; - varnumber_T n1 = list_find_nr(arg->vval.v_list, 0L, &error); - varnumber_T n2 = list_find_nr(arg->vval.v_list, 1L, &error); + bool error = false; + varnumber_T n1 = tv_list_find_nr(arg->vval.v_list, 0L, &error); + varnumber_T n2 = tv_list_find_nr(arg->vval.v_list, 1L, &error); if (error) { return FAIL; } @@ -14447,9 +13172,9 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr) STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u), "type punning will produce incorrect results on this platform"); - rettv_list_alloc(rettv); - list_append_number(rettv->vval.v_list, u.split.high); - list_append_number(rettv->vval.v_list, u.split.low); + tv_list_alloc_ret(rettv); + tv_list_append_number(rettv->vval.v_list, u.split.high); + tv_list_append_number(rettv->vval.v_list, u.split.low); } /// f_reltimestr - return a string that represents the value of {time} @@ -14479,7 +13204,6 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) listitem_T *li; long idx; long end; - char_u *key; dict_T *d; dictitem_T *di; const char *const arg_errmsg = _("remove() argument"); @@ -14490,18 +13214,18 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL && !tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len)) { - key = get_tv_string_chk(&argvars[1]); + const char *key = tv_get_string_chk(&argvars[1]); if (key != NULL) { - di = dict_find(d, key, -1); + di = tv_dict_find(d, key, -1); if (di == NULL) { EMSG2(_(e_dictkey), key); } else if (!var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len) && !var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { *rettv = di->di_tv; - init_tv(&di->di_tv); - dictitem_remove(d, di); - if (is_watched(d)) { - dictwatcher_notify(d, (char *)key, NULL, rettv); + di->di_tv = TV_INITIAL_VALUE; + tv_dict_item_remove(d, di); + if (tv_dict_is_watched(d)) { + tv_dict_watcher_notify(d, key, NULL, rettv); } } } @@ -14510,27 +13234,27 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listdictarg), "remove()"); } else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { - int error = (int)false; + bool error = false; - idx = get_tv_number_chk(&argvars[1], &error); - if (error) - ; /* type error: do nothing, errmsg already given */ - else if ((item = list_find(l, idx)) == NULL) + idx = tv_get_number_chk(&argvars[1], &error); + if (error) { + // Type error: do nothing, errmsg already given. + } else if ((item = tv_list_find(l, idx)) == NULL) { EMSGN(_(e_listidx), idx); - else { + } else { if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. - vim_list_remove(l, item, item); + tv_list_remove_items(l, item, item); *rettv = item->li_tv; xfree(item); } else { - /* Remove range of items, return list with values. */ - end = get_tv_number_chk(&argvars[2], &error); - if (error) - ; /* type error: do nothing */ - else if ((item2 = list_find(l, end)) == NULL) + // Remove range of items, return list with values. + end = tv_get_number_chk(&argvars[2], &error); + if (error) { + // Type error: do nothing. + } else if ((item2 = tv_list_find(l, end)) == NULL) { EMSGN(_(e_listidx), end); - else { + } else { int cnt = 0; for (li = item; li != NULL; li = li->li_next) { @@ -14538,11 +13262,11 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (li == item2) break; } - if (li == NULL) /* didn't find "item2" after "item" */ - EMSG(_(e_invrange)); - else { - vim_list_remove(l, item, item2); - l = rettv_list_alloc(rettv); + if (li == NULL) { // Didn't find "item2" after "item". + emsgf(_(e_invrange)); + } else { + tv_list_remove_items(l, item, item2); + l = tv_list_alloc_ret(rettv); l->lv_first = item; l->lv_last = item2; item->li_prev = NULL; @@ -14560,13 +13284,14 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { rettv->vval.v_number = -1; - else - rettv->vval.v_number = vim_rename(get_tv_string(&argvars[0]), - get_tv_string_buf(&argvars[1], buf)); + } else { + char buf[NUMBUFLEN]; + rettv->vval.v_number = vim_rename( + (const char_u *)tv_get_string(&argvars[0]), + (const char_u *)tv_get_string_buf(&argvars[1], buf)); + } } /* @@ -14574,32 +13299,37 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int n; - - n = get_tv_number(&argvars[1]); + varnumber_T n = tv_get_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { - rettv_list_alloc(rettv); - if (argvars[0].vval.v_list != NULL) { - while (n-- > 0) { - list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); - } + tv_list_alloc_ret(rettv); + while (n-- > 0) { + tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); } } else { - p = get_tv_string(&argvars[0]); rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; + if (n <= 0) { + return; + } + + const char *const p = tv_get_string(&argvars[0]); - int slen = (int)STRLEN(p); - int len = slen * n; - if (len <= 0) + const size_t slen = strlen(p); + if (slen == 0) { return; + } + const size_t len = slen * n; + // Detect overflow. + if (len / n != slen) { + return; + } - char_u *r = xmallocz(len); - for (int i = 0; i < n; i++) - memmove(r + i * slen, p, (size_t)slen); + char *const r = xmallocz(len); + for (varnumber_T i = 0; i < n; i++) { + memmove(r + i * slen, p, slen); + } - rettv->vval.v_string = r; + rettv->vval.v_string = (char_u *)r; } } @@ -14608,59 +13338,49 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; -#ifdef HAVE_READLINK - char_u *buf = NULL; -#endif - - p = get_tv_string(&argvars[0]); + rettv->v_type = VAR_STRING; + const char *fname = tv_get_string(&argvars[0]); #ifdef WIN32 - { - char *v = os_resolve_shortcut(p); - if (v != NULL) { - rettv->vval.v_string = (char_u *)v; - } else { - rettv->vval.v_string = vim_strsave(p); - } - } + char *const v = os_resolve_shortcut(fname); + rettv->vval.v_string = (char_u *)(v == NULL ? xstrdup(fname) : v); #else # ifdef HAVE_READLINK { - char_u *cpy; - int len; - char_u *remain = NULL; - char_u *q; - int is_relative_to_current = FALSE; - int has_trailing_pathsep = FALSE; + bool is_relative_to_current = false; + bool has_trailing_pathsep = false; int limit = 100; - p = vim_strsave(p); + char *p = xstrdup(fname); if (p[0] == '.' && (vim_ispathsep(p[1]) - || (p[1] == '.' && (vim_ispathsep(p[2]))))) - is_relative_to_current = TRUE; + || (p[1] == '.' && (vim_ispathsep(p[2]))))) { + is_relative_to_current = true; + } - len = STRLEN(p); - if (len > 0 && after_pathsep((char *)p, (char *)p + len)) { - has_trailing_pathsep = TRUE; - p[len - 1] = NUL; /* the trailing slash breaks readlink() */ + ptrdiff_t len = (ptrdiff_t)strlen(p); + if (len > 0 && after_pathsep(p, p + len)) { + has_trailing_pathsep = true; + p[len - 1] = NUL; // The trailing slash breaks readlink(). } - q = path_next_component(p); + char *q = (char *)path_next_component(p); + char *remain = NULL; if (*q != NUL) { - /* Separate the first path component in "p", and keep the - * remainder (beginning with the path separator). */ - remain = vim_strsave(q - 1); + // Separate the first path component in "p", and keep the + // remainder (beginning with the path separator). + remain = xstrdup(q - 1); q[-1] = NUL; } - buf = xmallocz(MAXPATHL); + char *const buf = xmallocz(MAXPATHL); + char *cpy; for (;; ) { for (;; ) { - len = readlink((char *)p, (char *)buf, MAXPATHL); - if (len <= 0) + len = readlink(p, buf, MAXPATHL); + if (len <= 0) { break; + } buf[len] = NUL; if (limit-- == 0) { @@ -14668,66 +13388,74 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(remain); EMSG(_("E655: Too many symbolic links (cycle?)")); rettv->vval.v_string = NULL; - goto fail; + xfree(buf); + return; } - /* Ensure that the result will have a trailing path separator - * if the argument has one. */ - if (remain == NULL && has_trailing_pathsep) - add_pathsep((char *)buf); + // Ensure that the result will have a trailing path separator + // if the argument has one. */ + if (remain == NULL && has_trailing_pathsep) { + add_pathsep(buf); + } - /* Separate the first path component in the link value and - * concatenate the remainders. */ - q = path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf); + // Separate the first path component in the link value and + // concatenate the remainders. */ + q = (char *)path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf); if (*q != NUL) { cpy = remain; - remain = remain ? - concat_str(q - 1, remain) : (char_u *) xstrdup((char *)q - 1); + remain = (remain + ? (char *)concat_str((char_u *)q - 1, (char_u *)remain) + : xstrdup(q - 1)); xfree(cpy); q[-1] = NUL; } - q = path_tail(p); + q = (char *)path_tail((char_u *)p); if (q > p && *q == NUL) { - /* Ignore trailing path separator. */ + // Ignore trailing path separator. q[-1] = NUL; - q = path_tail(p); + q = (char *)path_tail((char_u *)p); } - if (q > p && !path_is_absolute_path(buf)) { - /* symlink is relative to directory of argument */ - cpy = xmalloc(STRLEN(p) + STRLEN(buf) + 1); - STRCPY(cpy, p); - STRCPY(path_tail(cpy), buf); + if (q > p && !path_is_absolute_path((const char_u *)buf)) { + // Symlink is relative to directory of argument. + const size_t p_len = strlen(p); + const size_t buf_len = strlen(buf); + cpy = xmalloc(p_len + buf_len + 1); + memcpy(cpy, p, p_len); + memcpy(path_tail((char_u *)cpy), buf, buf_len + 1); xfree(p); p = cpy; } else { xfree(p); - p = vim_strsave(buf); + p = xstrdup(buf); } } - if (remain == NULL) + if (remain == NULL) { break; + } - /* Append the first path component of "remain" to "p". */ - q = path_next_component(remain + 1); + // Append the first path component of "remain" to "p". + q = (char *)path_next_component(remain + 1); len = q - remain - (*q != NUL); - cpy = vim_strnsave(p, STRLEN(p) + len); - STRNCAT(cpy, remain, len); + const size_t p_len = strlen(p); + cpy = xmallocz(p_len + len); + memcpy(cpy, p, p_len + 1); + xstrlcat(cpy + p_len, remain, len + 1); xfree(p); p = cpy; - /* Shorten "remain". */ - if (*q != NUL) + // Shorten "remain". + if (*q != NUL) { STRMOVE(remain, q - 1); - else { + } else { xfree(remain); remain = NULL; } } - /* If the result is a relative path name, make it explicitly relative to - * the current directory if and only if the argument had this form. */ + // If the result is a relative path name, make it explicitly relative to + // the current directory if and only if the argument had this form. if (!vim_ispathsep(*p)) { if (is_relative_to_current && *p != NUL @@ -14737,42 +13465,40 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) || (p[1] == '.' && (p[2] == NUL || vim_ispathsep(p[2])))))) { - /* Prepend "./". */ - cpy = concat_str((char_u *)"./", p); + // Prepend "./". + cpy = (char *)concat_str((const char_u *)"./", (const char_u *)p); xfree(p); p = cpy; } else if (!is_relative_to_current) { - /* Strip leading "./". */ + // Strip leading "./". q = p; - while (q[0] == '.' && vim_ispathsep(q[1])) + while (q[0] == '.' && vim_ispathsep(q[1])) { q += 2; - if (q > p) + } + if (q > p) { STRMOVE(p, p + 2); + } } } - /* Ensure that the result will have no trailing path separator - * if the argument had none. But keep "/" or "//". */ + // Ensure that the result will have no trailing path separator + // if the argument had none. But keep "/" or "//". if (!has_trailing_pathsep) { - q = p + STRLEN(p); - if (after_pathsep((char *)p, (char *)q)) - *path_tail_with_sep(p) = NUL; + q = p + strlen(p); + if (after_pathsep(p, q)) { + *path_tail_with_sep((char_u *)p) = NUL; + } } - rettv->vval.v_string = p; + rettv->vval.v_string = (char_u *)p; + xfree(buf); } # else - rettv->vval.v_string = vim_strsave(p); + rettv->vval.v_string = (char_u *)xstrdup(p); # endif #endif simplify_filename(rettv->vval.v_string); - -#ifdef HAVE_READLINK -fail: - xfree(buf); -#endif - rettv->v_type = VAR_STRING; } /* @@ -14793,12 +13519,12 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) l->lv_len = 0; while (li != NULL) { listitem_T *const ni = li->li_prev; - list_append(l, li); + tv_list_append(l, li); li = ni; } rettv->vval.v_list = l; rettv->v_type = VAR_LIST; - ++l->lv_refcount; + l->lv_refcount++; l->lv_idx = l->lv_len - l->lv_idx - 1; } } @@ -14820,40 +13546,45 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) static int get_search_arg(typval_T *varp, int *flagsp) { int dir = FORWARD; - char_u *flags; - char_u nbuf[NUMBUFLEN]; int mask; if (varp->v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf_chk(varp, nbuf); - if (flags == NULL) - return 0; /* type error; errmsg already given */ + char nbuf[NUMBUFLEN]; + const char *flags = tv_get_string_buf_chk(varp, nbuf); + if (flags == NULL) { + return 0; // Type error; errmsg already given. + } while (*flags != NUL) { switch (*flags) { - case 'b': dir = BACKWARD; break; - case 'w': p_ws = true; break; - case 'W': p_ws = false; break; - default: mask = 0; - if (flagsp != NULL) - switch (*flags) { - case 'c': mask = SP_START; break; - case 'e': mask = SP_END; break; - case 'm': mask = SP_RETCOUNT; break; - case 'n': mask = SP_NOMOVE; break; - case 'p': mask = SP_SUBPAT; break; - case 'r': mask = SP_REPEAT; break; - case 's': mask = SP_SETPCMARK; break; - case 'z': mask = SP_COLUMN; break; + case 'b': dir = BACKWARD; break; + case 'w': p_ws = true; break; + case 'W': p_ws = false; break; + default: { + mask = 0; + if (flagsp != NULL) { + switch (*flags) { + case 'c': mask = SP_START; break; + case 'e': mask = SP_END; break; + case 'm': mask = SP_RETCOUNT; break; + case 'n': mask = SP_NOMOVE; break; + case 'p': mask = SP_SUBPAT; break; + case 'r': mask = SP_REPEAT; break; + case 's': mask = SP_SETPCMARK; break; + case 'z': mask = SP_COLUMN; break; + } } - if (mask == 0) { - EMSG2(_(e_invarg2), flags); - dir = 0; - } else - *flagsp |= mask; + if (mask == 0) { + emsgf(_(e_invarg2), flags); + dir = 0; + } else { + *flagsp |= mask; + } + } } - if (dir == 0) + if (dir == 0) { break; - ++flags; + } + flags++; } } return dir; @@ -14863,7 +13594,6 @@ static int get_search_arg(typval_T *varp, int *flagsp) static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) { int flags; - char_u *pat; pos_T pos; pos_T save_cursor; bool save_p_ws = p_ws; @@ -14875,10 +13605,11 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) int options = SEARCH_KEEP; int subpatnum; - pat = get_tv_string(&argvars[0]); - dir = get_search_arg(&argvars[1], flagsp); /* may set p_ws */ - if (dir == 0) + const char *const pat = tv_get_string(&argvars[0]); + dir = get_search_arg(&argvars[1], flagsp); // May set p_ws. + if (dir == 0) { goto theend; + } flags = *flagsp; if (flags & SP_START) { options |= SEARCH_START; @@ -14892,13 +13623,15 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) /* Optional arguments: line number to stop searching and timeout. */ if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { - lnum_stop = get_tv_number_chk(&argvars[2], NULL); - if (lnum_stop < 0) + lnum_stop = tv_get_number_chk(&argvars[2], NULL); + if (lnum_stop < 0) { goto theend; + } if (argvars[3].v_type != VAR_UNKNOWN) { - time_limit = get_tv_number_chk(&argvars[3], NULL); - if (time_limit < 0) + time_limit = tv_get_number_chk(&argvars[3], NULL); + if (time_limit < 0) { goto theend; + } } } @@ -14913,13 +13646,13 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) */ if (((flags & (SP_REPEAT | SP_RETCOUNT)) != 0) || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[1])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[1])); goto theend; } pos = save_cursor = curwin->w_cursor; - subpatnum = searchit(curwin, curbuf, &pos, dir, pat, 1L, - options, RE_SEARCH, (linenr_T)lnum_stop, &tm); + subpatnum = searchit(curwin, curbuf, &pos, dir, (char_u *)pat, 1, + options, RE_SEARCH, (linenr_T)lnum_stop, &tm); if (subpatnum != FAIL) { if (flags & SP_SUBPAT) retval = subpatnum; @@ -14976,7 +13709,7 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (!channel_send_event((uint64_t)argvars[0].vval.v_number, - (char *)get_tv_string(&argvars[1]), + tv_get_string(&argvars[1]), args)) { EMSG2(_(e_invarg2), "Channel doesn't exist"); return; @@ -15043,7 +13776,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) Error err = ERROR_INIT; Object result = channel_send_call((uint64_t)argvars[0].vval.v_number, - (char *)get_tv_string(&argvars[1]), + tv_get_string(&argvars[1]), args, &err); @@ -15118,7 +13851,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Copy arguments to the vector if (argsl > 0) { for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - argv[i++] = xstrdup((char *) get_tv_string(&arg->li_tv)); + argv[i++] = xstrdup(tv_get_string(&arg->li_tv)); } } @@ -15160,17 +13893,16 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int row; - int col; int c; - row = get_tv_number_chk(&argvars[0], NULL) - 1; - col = get_tv_number_chk(&argvars[1], NULL) - 1; + const int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + const int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) + || col < 0 || col >= screen_Columns) { c = -1; - else + } else { c = ScreenAttrs[LineOffset[row] + col]; + } rettv->vval.v_number = c; } @@ -15179,17 +13911,15 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int row; - int col; int off; int c; - row = get_tv_number_chk(&argvars[0], NULL) - 1; - col = get_tv_number_chk(&argvars[1], NULL) - 1; + const int row = tv_get_number_chk(&argvars[0], NULL) - 1; + const int col = tv_get_number_chk(&argvars[1], NULL) - 1; if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) + || col < 0 || col >= screen_Columns) { c = -1; - else { + } else { off = LineOffset[row] + col; if (enc_utf8 && ScreenLinesUC[off] != 0) c = ScreenLinesUC[off]; @@ -15234,19 +13964,21 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int locally = 1; int thisblock = 0; - int error = FALSE; + bool error = false; rettv->vval.v_number = 1; /* default: FAIL */ - char_u *name = get_tv_string_chk(&argvars[0]); + const char *const name = tv_get_string_chk(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { - locally = get_tv_number_chk(&argvars[1], &error) == 0; - if (!error && argvars[2].v_type != VAR_UNKNOWN) - thisblock = get_tv_number_chk(&argvars[2], &error) != 0; + locally = tv_get_number_chk(&argvars[1], &error) == 0; + if (!error && argvars[2].v_type != VAR_UNKNOWN) { + thisblock = tv_get_number_chk(&argvars[2], &error) != 0; + } } - if (!error && name != NULL) - rettv->vval.v_number = find_decl(name, STRLEN(name), locally, + if (!error && name != NULL) { + rettv->vval.v_number = find_decl((char_u *)name, strlen(name), locally, thisblock, SEARCH_KEEP) == FAIL; + } } /* @@ -15254,65 +13986,70 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) { - char_u *spat, *mpat, *epat; - char_u *skip; bool save_p_ws = p_ws; int dir; int flags = 0; - char_u nbuf1[NUMBUFLEN]; - char_u nbuf2[NUMBUFLEN]; - char_u nbuf3[NUMBUFLEN]; - int retval = 0; /* default: FAIL */ + int retval = 0; // default: FAIL long lnum_stop = 0; long time_limit = 0; - /* Get the three pattern arguments: start, middle, end. */ - spat = get_tv_string_chk(&argvars[0]); - mpat = get_tv_string_buf_chk(&argvars[1], nbuf1); - epat = get_tv_string_buf_chk(&argvars[2], nbuf2); - if (spat == NULL || mpat == NULL || epat == NULL) - goto theend; /* type error */ + // Get the three pattern arguments: start, middle, end. + char nbuf1[NUMBUFLEN]; + char nbuf2[NUMBUFLEN]; + char nbuf3[NUMBUFLEN]; + const char *spat = tv_get_string_chk(&argvars[0]); + const char *mpat = tv_get_string_buf_chk(&argvars[1], nbuf1); + const char *epat = tv_get_string_buf_chk(&argvars[2], nbuf2); + if (spat == NULL || mpat == NULL || epat == NULL) { + goto theend; // Type error. + } - /* Handle the optional fourth argument: flags */ - dir = get_search_arg(&argvars[3], &flags); /* may set p_ws */ - if (dir == 0) + // Handle the optional fourth argument: flags. + dir = get_search_arg(&argvars[3], &flags); // may set p_ws. + if (dir == 0) { goto theend; + } - /* Don't accept SP_END or SP_SUBPAT. - * Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set. - */ + // Don't accept SP_END or SP_SUBPAT. + // Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set. if ((flags & (SP_END | SP_SUBPAT)) != 0 || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[3])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[3])); goto theend; } - /* Using 'r' implies 'W', otherwise it doesn't work. */ - if (flags & SP_REPEAT) + // Using 'r' implies 'W', otherwise it doesn't work. + if (flags & SP_REPEAT) { p_ws = false; + } - /* Optional fifth argument: skip expression */ + // Optional fifth argument: skip expression. + const char *skip; if (argvars[3].v_type == VAR_UNKNOWN - || argvars[4].v_type == VAR_UNKNOWN) - skip = (char_u *)""; - else { - skip = get_tv_string_buf_chk(&argvars[4], nbuf3); + || argvars[4].v_type == VAR_UNKNOWN) { + skip = ""; + } else { + skip = tv_get_string_buf_chk(&argvars[4], nbuf3); + if (skip == NULL) { + goto theend; // Type error. + } if (argvars[5].v_type != VAR_UNKNOWN) { - lnum_stop = get_tv_number_chk(&argvars[5], NULL); - if (lnum_stop < 0) + lnum_stop = tv_get_number_chk(&argvars[5], NULL); + if (lnum_stop < 0) { goto theend; + } if (argvars[6].v_type != VAR_UNKNOWN) { - time_limit = get_tv_number_chk(&argvars[6], NULL); - if (time_limit < 0) + time_limit = tv_get_number_chk(&argvars[6], NULL); + if (time_limit < 0) { goto theend; + } } } } - if (skip == NULL) - goto theend; /* type error */ - retval = do_searchpair(spat, mpat, epat, dir, skip, flags, - match_pos, lnum_stop, time_limit); + retval = do_searchpair( + (char_u *)spat, (char_u *)mpat, (char_u *)epat, dir, (char_u *)skip, + flags, match_pos, lnum_stop, time_limit); theend: p_ws = save_p_ws; @@ -15337,15 +14074,15 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int lnum = 0; int col = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (searchpair_cmn(argvars, &match_pos) > 0) { lnum = match_pos.lnum; col = match_pos.col; } - list_append_number(rettv->vval.v_list, (varnumber_T)lnum); - list_append_number(rettv->vval.v_list, (varnumber_T)col); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); } /* @@ -15377,7 +14114,6 @@ do_searchpair ( int n; int r; int nest = 1; - int err; int options = SEARCH_KEEP; proftime_T tm; @@ -15433,7 +14169,8 @@ do_searchpair ( if (*skip != NUL) { save_pos = curwin->w_cursor; curwin->w_cursor = pos; - r = eval_to_bool(skip, &err, NULL, FALSE); + bool err; + r = eval_to_bool(skip, &err, NULL, false); curwin->w_cursor = save_pos; if (err) { /* Evaluating {skip} caused an error, break here. */ @@ -15504,7 +14241,7 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n; int flags = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); n = search_cmn(argvars, &match_pos, &flags); if (n > 0) { @@ -15512,10 +14249,11 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) col = match_pos.col; } - list_append_number(rettv->vval.v_list, (varnumber_T)lnum); - list_append_number(rettv->vval.v_list, (varnumber_T)col); - if (flags & SP_SUBPAT) - list_append_number(rettv->vval.v_list, (varnumber_T)n); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); + if (flags & SP_SUBPAT) { + tv_list_append_number(rettv->vval.v_list, (varnumber_T)n); + } } /// "serverlist()" function @@ -15525,13 +14263,13 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **addrs = server_address_list(&n); // Copy addrs into a linked list. - list_T *l = rettv_list_alloc(rettv); + list_T *l = tv_list_alloc_ret(rettv); for (size_t i = 0; i < n; i++) { - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *) addrs[i]; - list_append(l, li); + li->li_tv.vval.v_string = (char_u *)addrs[i]; + tv_list_append(l, li); } xfree(addrs); } @@ -15552,7 +14290,7 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_invarg)); return; } else { - rettv->vval.v_string = vim_strsave(get_tv_string(argvars)); + rettv->vval.v_string = (char_u *)xstrdup(tv_get_string(argvars)); } } else { rettv->vval.v_string = (char_u *)server_address_new(); @@ -15586,32 +14324,30 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u nbuf[NUMBUFLEN]; - if (check_restricted() || check_secure() || !tv_check_str_or_nr(&argvars[0])) { return; } - const char *varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *varname = tv_get_string_chk(&argvars[1]); buf_T *const buf = get_buf_tv(&argvars[0], false); typval_T *varp = &argvars[2]; if (buf != NULL && varname != NULL && varp != NULL) { if (*varname == '&') { long numval; - char_u *strval; - int error = false; + bool error = false; aco_save_T aco; // set curbuf to be our buf, temporarily aucmd_prepbuf(&aco, buf); varname++; - numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); + numval = tv_get_number_chk(varp, &error); + char nbuf[NUMBUFLEN]; + const char *const strval = tv_get_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { - set_option_value((char_u *)varname, numval, strval, OPT_LOCAL); + set_option_value(varname, numval, strval, OPT_LOCAL); } // reset notion of buffer @@ -15620,11 +14356,11 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) buf_T *save_curbuf = curbuf; const size_t varname_len = STRLEN(varname); - char_u *const bufvarname = xmalloc(STRLEN(varname) + 3); + char *const bufvarname = xmalloc(varname_len + 3); curbuf = buf; memcpy(bufvarname, "b:", 2); memcpy(bufvarname + 2, varname, varname_len + 1); - set_var(bufvarname, varp, true); + set_var(bufvarname, varname_len + 2, varp, true); xfree(bufvarname); curbuf = save_curbuf; } @@ -15635,7 +14371,6 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *d; dictitem_T *di; - char_u *csearch; if (argvars[0].v_type != VAR_DICT) { EMSG(_(e_dictreq)); @@ -15643,7 +14378,7 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if ((d = argvars[0].vval.v_dict) != NULL) { - csearch = get_dict_string(d, "char", false); + char_u *const csearch = (char_u *)tv_dict_get_string(d, "char", false); if (csearch != NULL) { if (enc_utf8) { int pcc[MAX_MCO]; @@ -15655,13 +14390,15 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) csearch, MB_PTR2LEN(csearch)); } - di = dict_find(d, (char_u *)"forward", -1); - if (di != NULL) - set_csearch_direction(get_tv_number(&di->di_tv) ? FORWARD : BACKWARD); + di = tv_dict_find(d, S_LEN("forward")); + if (di != NULL) { + set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD); + } - di = dict_find(d, (char_u *)"until", -1); - if (di != NULL) - set_csearch_until(!!get_tv_number(&di->di_tv)); + di = tv_dict_find(d, S_LEN("until")); + if (di != NULL) { + set_csearch_until(!!tv_get_number(&di->di_tv)); + } } } @@ -15670,10 +14407,11 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int pos = (int)get_tv_number(&argvars[0]) - 1; + const int pos = (int)tv_get_number(&argvars[0]) - 1; - if (pos >= 0) + if (pos >= 0) { rettv->vval.v_number = set_cmdline_pos(pos); + } } @@ -15682,17 +14420,17 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = 0; - char_u *fname = get_tv_string_chk(&argvars[0]); + const char *const fname = tv_get_string_chk(&argvars[0]); if (fname == NULL) { return; } - char_u modebuf[NUMBUFLEN]; - char_u *mode_str = get_tv_string_buf_chk(&argvars[1], modebuf); + char modebuf[NUMBUFLEN]; + const char *const mode_str = tv_get_string_buf_chk(&argvars[1], modebuf); if (mode_str == NULL) { return; } - if (STRLEN(mode_str) != 9) { + if (strlen(mode_str) != 9) { EMSG2(_(e_invarg2), mode_str); return; } @@ -15713,27 +14451,28 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - char_u *line = NULL; list_T *l = NULL; listitem_T *li = NULL; long added = 0; linenr_T lcount = curbuf->b_ml.ml_line_count; - lnum = get_tv_lnum(&argvars[0]); + linenr_T lnum = tv_get_lnum(&argvars[0]); + const char *line = NULL; if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; li = l->lv_first; - } else - line = get_tv_string_chk(&argvars[1]); + } else { + line = tv_get_string_chk(&argvars[1]); + } /* default result is zero == OK */ for (;; ) { if (l != NULL) { - /* list argument, get next string */ - if (li == NULL) + // List argument, get next string. + if (li == NULL) { break; - line = get_tv_string_chk(&li->li_tv); + } + line = tv_get_string_chk(&li->li_tv); li = li->li_next; } @@ -15749,18 +14488,20 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (lnum <= curbuf->b_ml.ml_line_count) { - /* existing line, replace it */ - if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK) { + // Existing line, replace it. + if (u_savesub(lnum) == OK + && ml_replace(lnum, (char_u *)line, true) == OK) { changed_bytes(lnum, 0); if (lnum == curwin->w_cursor.lnum) check_cursor_col(); rettv->vval.v_number = 0; /* OK */ } } else if (added > 0 || u_save(lnum - 1, lnum) == OK) { - /* lnum is one past the last line, append the line */ - ++added; - if (ml_append(lnum - 1, line, (colnr_T)0, FALSE) == OK) - rettv->vval.v_number = 0; /* OK */ + // lnum is one past the last line, append the line. + added++; + if (ml_append(lnum - 1, (char_u *)line, 0, false) == OK) { + rettv->vval.v_number = 0; // OK + } } if (l == NULL) /* only one string argument */ @@ -15789,7 +14530,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(2, 3) { static char *e_invact = N_("E927: Invalid action: '%s'"); - char_u *title = NULL; + const char *title = NULL; int action = ' '; rettv->vval.v_number = -1; dict_T *d = NULL; @@ -15808,7 +14549,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) EMSG(_(e_stringreq)); return; } - char_u *act = get_tv_string_chk(action_arg); + const char *const act = tv_get_string_chk(action_arg); if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) { action = *act; } else { @@ -15821,25 +14562,25 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) // Option argument was not given. goto skip_args; } else if (title_arg->v_type == VAR_STRING) { - title = get_tv_string_chk(title_arg); + title = tv_get_string_chk(title_arg); if (!title) { - // Type error. Error already printed by get_tv_string_chk(). + // Type error. Error already printed by tv_get_string_chk(). return; } } else if (title_arg->v_type == VAR_DICT) { d = title_arg->vval.v_dict; } else { - EMSG(_(e_dictreq)); + emsgf(_(e_dictreq)); return; } skip_args: if (!title) { - title = (char_u*)(wp ? "setloclist()" : "setqflist()"); + title = (wp ? "setloclist()" : "setqflist()"); } list_T *l = list_arg->vval.v_list; - if (l && set_errorlist(wp, l, action, title, d) == OK) { + if (l && set_errorlist(wp, l, action, (char_u *)title, d) == OK) { rettv->vval.v_number = 0; } } @@ -15885,11 +14626,11 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_invarg)); return; } - if (!(dict_find(d, (char_u *)"group", -1) != NULL - && (dict_find(d, (char_u *)"pattern", -1) != NULL - || dict_find(d, (char_u *)"pos1", -1) != NULL) - && dict_find(d, (char_u *)"priority", -1) != NULL - && dict_find(d, (char_u *)"id", -1) != NULL)) { + if (!(tv_dict_find(d, S_LEN("group")) != NULL + && (tv_dict_find(d, S_LEN("pattern")) != NULL + || tv_dict_find(d, S_LEN("pos1")) != NULL) + && tv_dict_find(d, S_LEN("priority")) != NULL + && tv_dict_find(d, S_LEN("id")) != NULL)) { EMSG(_(e_invarg)); return; } @@ -15898,29 +14639,31 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) clear_matches(curwin); li = l->lv_first; + bool match_add_failed = false; while (li != NULL) { int i = 0; - char_u buf[5]; - dictitem_T *di; d = li->li_tv.vval.v_dict; - if (dict_find(d, (char_u *)"pattern", -1) == NULL) { + dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); + if (di == NULL) { if (s == NULL) { - s = list_alloc(); + s = tv_list_alloc(); if (s == NULL) { return; } } // match from matchaddpos() - for (i = 1; i < 9; ++i) { - snprintf((char *)buf, sizeof(buf), (char *)"pos%d", i); - if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) { - if (di->di_tv.v_type != VAR_LIST) { + for (i = 1; i < 9; i++) { + char buf[5]; + snprintf(buf, sizeof(buf), "pos%d", i); + dictitem_T *const pos_di = tv_dict_find(d, buf, -1); + if (pos_di != NULL) { + if (pos_di->di_tv.v_type != VAR_LIST) { return; } - list_append_tv(s, &di->di_tv); + tv_list_append_tv(s, &pos_di->di_tv); s->lv_refcount++; } else { break; @@ -15928,26 +14671,39 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - char_u *group = get_dict_string(d, "group", true); - int priority = get_dict_number(d, "priority"); - int id = get_dict_number(d, "id"); - char_u *conceal = dict_find(d, (char_u *)"conceal", -1) != NULL - ? get_dict_string(d, "conceal", true) - : NULL; + // Note: there are three number buffers involved: + // - group_buf below. + // - numbuf in tv_dict_get_string(). + // - mybuf in tv_get_string(). + // + // If you change this code make sure that buffers will not get + // accidentally reused. + char group_buf[NUMBUFLEN]; + const char *const group = tv_dict_get_string_buf(d, "group", group_buf); + const int priority = (int)tv_dict_get_number(d, "priority"); + const int id = (int)tv_dict_get_number(d, "id"); + dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); + const char *const conceal = (conceal_di != NULL + ? tv_get_string(&conceal_di->di_tv) + : NULL); if (i == 0) { - match_add(curwin, group, - get_dict_string(d, "pattern", false), - priority, id, NULL, conceal); + if (match_add(curwin, group, + tv_dict_get_string(d, "pattern", false), + priority, id, NULL, conceal) != id) { + match_add_failed = true; + } } else { - match_add(curwin, group, NULL, priority, id, s, conceal); - list_unref(s); + if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { + match_add_failed = true; + } + tv_list_unref(s); s = NULL; } - xfree(group); - xfree(conceal); li = li->li_next; } - rettv->vval.v_number = 0; + if (!match_add_failed) { + rettv->vval.v_number = 0; + } } } @@ -15958,11 +14714,10 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T pos; int fnum; - char_u *name; colnr_T curswant = -1; rettv->vval.v_number = -1; - name = get_tv_string_chk(argvars); + const char *const name = tv_get_string_chk(argvars); if (name != NULL) { if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) { if (--pos.col < 0) { @@ -15983,7 +14738,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { // set mark - if (setmark_pos(name[1], &pos, fnum) == OK) { + if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) { rettv->vval.v_number = 0; } } else { @@ -16007,8 +14762,6 @@ static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int regname; - char_u *strregname; - char_u *stropt; bool append = false; MotionType yank_type; long block_len; @@ -16016,39 +14769,47 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) block_len = -1; yank_type = kMTUnknown; - strregname = get_tv_string_chk(argvars); - rettv->vval.v_number = 1; /* FAIL is default */ + rettv->vval.v_number = 1; // FAIL is default. - if (strregname == NULL) - return; /* type error; errmsg already given */ - regname = *strregname; - if (regname == 0 || regname == '@') + const char *const strregname = tv_get_string_chk(argvars); + if (strregname == NULL) { + return; // Type error; errmsg already given. + } + regname = (uint8_t)(*strregname); + if (regname == 0 || regname == '@') { regname = '"'; + } if (argvars[2].v_type != VAR_UNKNOWN) { - stropt = get_tv_string_chk(&argvars[2]); - if (stropt == NULL) - return; /* type error */ - for (; *stropt != NUL; ++stropt) + const char *stropt = tv_get_string_chk(&argvars[2]); + if (stropt == NULL) { + return; // Type error. + } + for (; *stropt != NUL; stropt++) { switch (*stropt) { - case 'a': case 'A': // append - append = true; - break; - case 'v': case 'c': // character-wise selection - yank_type = kMTCharWise; - break; - case 'V': case 'l': // line-wise selection - yank_type = kMTLineWise; - break; - case 'b': case Ctrl_V: // block-wise selection - yank_type = kMTBlockWise; - if (ascii_isdigit(stropt[1])) { - ++stropt; - block_len = getdigits_long(&stropt) - 1; - --stropt; + case 'a': case 'A': { // append + append = true; + break; + } + case 'v': case 'c': { // character-wise selection + yank_type = kMTCharWise; + break; + } + case 'V': case 'l': { // line-wise selection + yank_type = kMTLineWise; + break; + } + case 'b': case Ctrl_V: { // block-wise selection + yank_type = kMTBlockWise; + if (ascii_isdigit(stropt[1])) { + stropt++; + block_len = getdigits_long((char_u **)&stropt) - 1; + stropt--; + } + break; } - break; } + } } if (argvars[1].v_type == VAR_LIST) { @@ -16058,42 +14819,44 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) // First half: use for pointers to result lines; second half: use for // pointers to allocated copies. - char_u **lstval = xmalloc(sizeof(char_u *) * ((len + 1) * 2)); - char_u **curval = lstval; - char_u **allocval = lstval + len + 2; - char_u **curallocval = allocval; + char **lstval = xmalloc(sizeof(char *) * ((len + 1) * 2)); + const char **curval = (const char **)lstval; + char **allocval = lstval + len + 2; + char **curallocval = allocval; - char_u buf[NUMBUFLEN]; for (listitem_T *li = ll == NULL ? NULL : ll->lv_first; li != NULL; li = li->li_next) { - char_u *strval = get_tv_string_buf_chk(&li->li_tv, buf); - if (strval == NULL) { + char buf[NUMBUFLEN]; + *curval = tv_get_string_buf_chk(&li->li_tv, buf); + if (*curval == NULL) { goto free_lstval; } - if (strval == buf) { + if (*curval == buf) { // Need to make a copy, - // next get_tv_string_buf_chk() will overwrite the string. - strval = vim_strsave(buf); - *curallocval++ = strval; + // next tv_get_string_buf_chk() will overwrite the string. + *curallocval = xstrdup(*curval); + *curval = *curallocval; + curallocval++; } - *curval++ = strval; + curval++; } *curval++ = NULL; - write_reg_contents_lst(regname, lstval, STRLEN(lstval), - append, yank_type, block_len); + write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, + block_len); free_lstval: - while (curallocval > allocval) - xfree(*--curallocval); + while (curallocval > allocval) { + xfree(*--curallocval); + } xfree(lstval); } else { - char_u *strval = get_tv_string_chk(&argvars[1]); + const char *strval = tv_get_string_chk(&argvars[1]); if (strval == NULL) { return; } - write_reg_contents_ex(regname, strval, STRLEN(strval), + write_reg_contents_ex(regname, (const char_u *)strval, STRLEN(strval), append, yank_type, block_len); } rettv->vval.v_number = 0; @@ -16104,35 +14867,31 @@ free_lstval: */ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tabpage_T *save_curtab; - tabpage_T *tp; - char_u *varname, *tabvarname; - typval_T *varp; - rettv->vval.v_number = 0; - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { return; + } - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - varname = get_tv_string_chk(&argvars[1]); - varp = &argvars[2]; + tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + const char *const varname = tv_get_string_chk(&argvars[1]); + typval_T *const varp = &argvars[2]; - if (varname != NULL && varp != NULL - && tp != NULL - ) { - save_curtab = curtab; - goto_tabpage_tp(tp, FALSE, FALSE); + if (varname != NULL && varp != NULL && tp != NULL) { + tabpage_T *const save_curtab = curtab; + goto_tabpage_tp(tp, false, false); - tabvarname = xmalloc(STRLEN(varname) + 3); - STRCPY(tabvarname, "t:"); - STRCPY(tabvarname + 2, varname); - set_var(tabvarname, varp, TRUE); + const size_t varname_len = strlen(varname); + char *const tabvarname = xmalloc(varname_len + 3); + memcpy(tabvarname, "t:", 2); + memcpy(tabvarname + 2, varname, varname_len + 1); + set_var(tabvarname, varname_len + 2, varp, true); xfree(tabvarname); - /* Restore current tabpage */ - if (valid_tabpage(save_curtab)) - goto_tabpage_tp(save_curtab, FALSE, FALSE); + // Restore current tabpage. + if (valid_tabpage(save_curtab)) { + goto_tabpage_tp(save_curtab, false, false); + } } } @@ -16158,45 +14917,43 @@ static void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void setwinvar(typval_T *argvars, typval_T *rettv, int off) { - win_T *win; - win_T *save_curwin; - tabpage_T *save_curtab; - char_u *varname, *winvarname; - typval_T *varp; - char_u nbuf[NUMBUFLEN]; - tabpage_T *tp = NULL; - - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { return; + } - if (off == 1) - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else + tabpage_T *tp = NULL; + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { tp = curtab; - win = find_win_by_nr(&argvars[off], tp); - varname = get_tv_string_chk(&argvars[off + 1]); - varp = &argvars[off + 2]; + } + win_T *const win = find_win_by_nr(&argvars[off], tp); + const char *varname = tv_get_string_chk(&argvars[off + 1]); + typval_T *varp = &argvars[off + 2]; if (win != NULL && varname != NULL && varp != NULL) { + win_T *save_curwin; + tabpage_T *save_curtab; bool need_switch_win = tp != curtab || win != curwin; if (!need_switch_win || switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) { if (*varname == '&') { long numval; - char_u *strval; - int error = false; + bool error = false; - ++varname; - numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); + varname++; + numval = tv_get_number_chk(varp, &error); + char nbuf[NUMBUFLEN]; + const char *const strval = tv_get_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } } else { - winvarname = xmalloc(STRLEN(varname) + 3); - STRCPY(winvarname, "w:"); - STRCPY(winvarname + 2, varname); - set_var(winvarname, varp, true); + const size_t varname_len = strlen(varname); + char *const winvarname = xmalloc(varname_len + 3); + memcpy(winvarname, "w:", 2); + memcpy(winvarname + 2, varname, varname_len + 1); + set_var(winvarname, varname_len + 2, varp, true); xfree(winvarname); } } @@ -16209,11 +14966,11 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) /// f_sha256 - sha256({string}) function static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = get_tv_string(&argvars[0]); - const char_u *hash = sha256_bytes(p, (int) STRLEN(p) , NULL, 0); + const char *p = tv_get_string(&argvars[0]); + const char *hash = sha256_bytes((const uint8_t *)p, strlen(p) , NULL, 0); // make a copy of the hash (sha256_bytes returns a static buffer) - rettv->vval.v_string = (char_u *) xstrdup((char *) hash); + rettv->vval.v_string = (char_u *)xstrdup(hash); rettv->v_type = VAR_STRING; } @@ -16223,7 +14980,8 @@ static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_string = vim_strsave_shellescape( - get_tv_string(&argvars[0]), non_zero_arg(&argvars[1]), true); + (const char_u *)tv_get_string(&argvars[0]), non_zero_arg(&argvars[1]), + true); rettv->v_type = VAR_STRING; } @@ -16240,11 +14998,9 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - - p = get_tv_string(&argvars[0]); - rettv->vval.v_string = vim_strsave(p); - simplify_filename(rettv->vval.v_string); /* simplify in place */ + const char *const p = tv_get_string(&argvars[0]); + rettv->vval.v_string = (char_u *)xstrdup(p); + simplify_filename(rettv->vval.v_string); // Simplify in place. rettv->v_type = VAR_STRING; } @@ -16260,10 +15016,10 @@ typedef struct { bool item_compare_numeric; bool item_compare_numbers; bool item_compare_float; - char_u *item_compare_func; + const char *item_compare_func; partial_T *item_compare_partial; dict_T *item_compare_selfdict; - int item_compare_func_err; + bool item_compare_func_err; } sortinfo_T; static sortinfo_T *sortinfo = NULL; @@ -16274,58 +15030,61 @@ static sortinfo_T *sortinfo = NULL; */ static int item_compare(const void *s1, const void *s2, bool keep_zero) { - sortItem_T *si1, *si2; - char_u *p1; - char_u *p2; - char_u *tofree1 = NULL; - char_u *tofree2 = NULL; - int res; + sortItem_T *const si1 = (sortItem_T *)s1; + sortItem_T *const si2 = (sortItem_T *)s2; - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; - typval_T *tv1 = &si1->item->li_tv; - typval_T *tv2 = &si2->item->li_tv; + typval_T *const tv1 = &si1->item->li_tv; + typval_T *const tv2 = &si2->item->li_tv; + + int res; if (sortinfo->item_compare_numbers) { - long v1 = get_tv_number(tv1); - long v2 = get_tv_number(tv2); + const long v1 = tv_get_number(tv1); + const long v2 = tv_get_number(tv2); - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; } if (sortinfo->item_compare_float) { - float_T v1 = get_tv_float(tv1); - float_T v2 = get_tv_float(tv2); + const float_T v1 = tv_get_float(tv1); + const float_T v2 = tv_get_float(tv2); - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; } + char *tofree1 = NULL; + char *tofree2 = NULL; + char *p1; + char *p2; + // encode_tv2string() puts quotes around a string and allocates memory. Don't // do that for string variables. Use a single quote when comparing with // a non-string to do what the docs promise. if (tv1->v_type == VAR_STRING) { if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p1 = (char_u *)"'"; + p1 = "'"; } else { - p1 = tv1->vval.v_string; + p1 = (char *)tv1->vval.v_string; } } else { - tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL); + tofree1 = p1 = encode_tv2string(tv1, NULL); } if (tv2->v_type == VAR_STRING) { if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p2 = (char_u *)"'"; + p2 = "'"; } else { - p2 = tv2->vval.v_string; + p2 = (char *)tv2->vval.v_string; } } else { - tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL); + tofree2 = p2 = encode_tv2string(tv2, NULL); } if (p1 == NULL) { - p1 = (char_u *)""; + p1 = ""; } if (p2 == NULL) { - p2 = (char_u *)""; + p2 = ""; } if (!sortinfo->item_compare_numeric) { if (sortinfo->item_compare_ic) { @@ -16335,19 +15094,20 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) } } else { double n1, n2; - n1 = strtod((char *)p1, (char **)&p1); - n2 = strtod((char *)p2, (char **)&p2); + n1 = strtod(p1, &p1); + n2 = strtod(p2, &p2); res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; } + xfree(tofree1); + xfree(tofree2); + +item_compare_end: // When the result would be zero, compare the item indexes. Makes the // sort stable. if (res == 0 && !keep_zero) { res = si1->idx > si2->idx ? 1 : -1; } - - xfree(tofree1); - xfree(tofree2); return res; } @@ -16368,7 +15128,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) typval_T rettv; typval_T argv[3]; int dummy; - char_u *func_name; + const char *func_name; partial_T *partial = sortinfo->item_compare_partial; // shortcut after failure in previous call; compare all items equal @@ -16382,31 +15142,31 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) if (partial == NULL) { func_name = sortinfo->item_compare_func; } else { - func_name = partial_name(partial); + func_name = (const char *)partial_name(partial); } // Copy the values. This is needed to be able to set v_lock to VAR_FIXED // in the copy without changing the original list items. - copy_tv(&si1->item->li_tv, &argv[0]); - copy_tv(&si2->item->li_tv, &argv[1]); + tv_copy(&si1->item->li_tv, &argv[0]); + tv_copy(&si2->item->li_tv, &argv[1]); - rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this - res = call_func(func_name, + rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this + res = call_func((const char_u *)func_name, (int)STRLEN(func_name), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, partial, sortinfo->item_compare_selfdict); - clear_tv(&argv[0]); - clear_tv(&argv[1]); + tv_clear(&argv[0]); + tv_clear(&argv[1]); if (res == FAIL) { res = ITEM_COMPARE_FAIL; } else { - res = get_tv_number_chk(&rettv, &sortinfo->item_compare_func_err); + res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); } if (sortinfo->item_compare_func_err) { res = ITEM_COMPARE_FAIL; // return value has wrong type } - clear_tv(&rettv); + tv_clear(&rettv); // When the result would be zero, compare the pointers themselves. Makes // the sort stable. @@ -16461,7 +15221,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) rettv->v_type = VAR_LIST; ++l->lv_refcount; - len = list_len(l); + len = tv_list_len(l); if (len <= 1) { goto theend; // short list sorts pretty quickly } @@ -16477,20 +15237,20 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (argvars[1].v_type != VAR_UNKNOWN) { /* optional second argument: {func} */ if (argvars[1].v_type == VAR_FUNC) { - info.item_compare_func = argvars[1].vval.v_string; + info.item_compare_func = (const char *)argvars[1].vval.v_string; } else if (argvars[1].v_type == VAR_PARTIAL) { info.item_compare_partial = argvars[1].vval.v_partial; } else { - int error = FALSE; + bool error = false; - i = get_tv_number_chk(&argvars[1], &error); + i = tv_get_number_chk(&argvars[1], &error); if (error) { goto theend; // type error; errmsg already given } if (i == 1) { info.item_compare_ic = true; } else if (argvars[1].v_type != VAR_NUMBER) { - info.item_compare_func = get_tv_string(&argvars[1]); + info.item_compare_func = tv_get_string(&argvars[1]); } else if (i != 0) { EMSG(_(e_invarg)); goto theend; @@ -16499,16 +15259,16 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (*info.item_compare_func == NUL) { // empty string means default sort info.item_compare_func = NULL; - } else if (STRCMP(info.item_compare_func, "n") == 0) { + } else if (strcmp(info.item_compare_func, "n") == 0) { info.item_compare_func = NULL; info.item_compare_numeric = true; - } else if (STRCMP(info.item_compare_func, "N") == 0) { + } else if (strcmp(info.item_compare_func, "N") == 0) { info.item_compare_func = NULL; info.item_compare_numbers = true; - } else if (STRCMP(info.item_compare_func, "f") == 0) { + } else if (strcmp(info.item_compare_func, "f") == 0) { info.item_compare_func = NULL; info.item_compare_float = true; - } else if (STRCMP(info.item_compare_func, "i") == 0) { + } else if (strcmp(info.item_compare_func, "i") == 0) { info.item_compare_func = NULL; info.item_compare_ic = true; } @@ -16560,7 +15320,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) l->lv_len = 0; for (i = 0; i < len; i++) { - list_append(l, ptrs[i].item); + tv_list_append(l, ptrs[i].item); } } } @@ -16596,8 +15356,8 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else { l->lv_last = ptrs[i].item; } - list_fix_watch(l, li); - listitem_free(li); + tv_list_watch_fix(l, li); + tv_list_item_free(li); l->lv_len--; } } @@ -16642,11 +15402,9 @@ static void f_reltimefloat(typval_T *argvars , typval_T *rettv, FunPtr fptr) */ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; - rettv->v_type = VAR_STRING; - s = get_tv_string(&argvars[0]); - rettv->vval.v_string = eval_soundfold(s); + const char *const s = tv_get_string(&argvars[0]); + rettv->vval.v_string = (char_u *)eval_soundfold(s); } /* @@ -16654,25 +15412,26 @@ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *word = (char_u *)""; + const char *word = ""; hlf_T attr = HLF_COUNT; size_t len = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type == VAR_UNKNOWN) { - /* Find the start and length of the badly spelled word. */ - len = spell_move_to(curwin, FORWARD, TRUE, TRUE, &attr); - if (len != 0) - word = get_cursor_pos_ptr(); + // Find the start and length of the badly spelled word. + len = spell_move_to(curwin, FORWARD, true, true, &attr); + if (len != 0) { + word = (char *)get_cursor_pos_ptr(); + } } else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) { - char_u *str = get_tv_string_chk(&argvars[0]); + const char *str = tv_get_string_chk(&argvars[0]); int capcol = -1; if (str != NULL) { - /* Check the argument for spelling. */ + // Check the argument for spelling. while (*str != NUL) { - len = spell_check(curwin, str, &attr, &capcol, false); + len = spell_check(curwin, (char_u *)str, &attr, &capcol, false); if (attr != HLF_COUNT) { word = str; break; @@ -16683,14 +15442,13 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } assert(len <= INT_MAX); - list_append_string(rettv->vval.v_list, word, (int)len); - list_append_string(rettv->vval.v_list, - (char_u *)(attr == HLF_SPB ? "bad" : - attr == HLF_SPR ? "rare" : - attr == HLF_SPL ? "local" : - attr == HLF_SPC ? "caps" : - ""), - -1); + tv_list_append_string(rettv->vval.v_list, word, len); + tv_list_append_string(rettv->vval.v_list, + (attr == HLF_SPB ? "bad" + : attr == HLF_SPR ? "rare" + : attr == HLF_SPL ? "local" + : attr == HLF_SPC ? "caps" + : NULL), -1); } /* @@ -16698,39 +15456,40 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; - int typeerr = FALSE; + bool typeerr = false; int maxcount; garray_T ga; listitem_T *li; bool need_capital = false; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { - str = get_tv_string(&argvars[0]); + const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { - maxcount = get_tv_number_chk(&argvars[1], &typeerr); - if (maxcount <= 0) + maxcount = tv_get_number_chk(&argvars[1], &typeerr); + if (maxcount <= 0) { return; + } if (argvars[2].v_type != VAR_UNKNOWN) { - need_capital = get_tv_number_chk(&argvars[2], &typeerr); - if (typeerr) + need_capital = tv_get_number_chk(&argvars[2], &typeerr); + if (typeerr) { return; + } } } else maxcount = 25; - spell_suggest_list(&ga, str, maxcount, need_capital, false); + spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false); - for (int i = 0; i < ga.ga_len; ++i) { - str = ((char_u **)ga.ga_data)[i]; + for (int i = 0; i < ga.ga_len; i++) { + char *p = ((char **)ga.ga_data)[i]; - li = listitem_alloc(); + li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = str; - list_append(rettv->vval.v_list, li); + li->li_tv.vval.v_string = (char_u *)p; + tv_list_append(rettv->vval.v_list, li); } ga_clear(&ga); } @@ -16738,64 +15497,69 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; - char_u *end; - char_u *pat = NULL; regmatch_T regmatch; - char_u patbuf[NUMBUFLEN]; char_u *save_cpo; int match; colnr_T col = 0; - int keepempty = FALSE; - int typeerr = FALSE; + bool keepempty = false; + bool typeerr = false; /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ save_cpo = p_cpo; p_cpo = (char_u *)""; - str = get_tv_string(&argvars[0]); + const char *str = tv_get_string(&argvars[0]); + const char *pat = NULL; + char patbuf[NUMBUFLEN]; if (argvars[1].v_type != VAR_UNKNOWN) { - pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) - typeerr = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) - keepempty = get_tv_number_chk(&argvars[2], &typeerr); + pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) { + typeerr = true; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr); + } + } + if (pat == NULL || *pat == NUL) { + pat = "[\\x01- ]\\+"; } - if (pat == NULL || *pat == NUL) - pat = (char_u *)"[\\x01- ]\\+"; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (typeerr) return; - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = FALSE; while (*str != NUL || keepempty) { - if (*str == NUL) - match = FALSE; /* empty item at the end */ - else - match = vim_regexec_nl(®match, str, col); - if (match) - end = regmatch.startp[0]; - else - end = str + STRLEN(str); + if (*str == NUL) { + match = false; // Empty item at the end. + } else { + match = vim_regexec_nl(®match, (char_u *)str, col); + } + const char *end; + if (match) { + end = (const char *)regmatch.startp[0]; + } else { + end = str + strlen(str); + } if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 - && *str != NUL && match && end < - regmatch.endp[0])) { - list_append_string(rettv->vval.v_list, str, (int)(end - str)); + && *str != NUL + && match + && end < (const char *)regmatch.endp[0])) { + tv_list_append_string(rettv->vval.v_list, str, end - str); } if (!match) break; - /* Advance to just after the match. */ - if (regmatch.endp[0] > str) + // Advance to just after the match. + if (regmatch.endp[0] > (char_u *)str) { col = 0; - else { - /* Don't get stuck at the same match. */ + } else { + // Don't get stuck at the same match. col = (*mb_ptr2len)(regmatch.endp[0]); } - str = regmatch.endp[0]; + str = (const char *)regmatch.endp[0]; } vim_regfree(regmatch.regprog); @@ -16809,11 +15573,12 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = skipwhite(get_tv_string(&argvars[0])); + char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); - if (*p == '+') + if (*p == '+') { p = skipwhite(p + 1); - (void) string2float((char *) p, &rettv->vval.v_float); + } + (void)string2float((char *)p, &rettv->vval.v_float); rettv->v_type = VAR_FLOAT; } @@ -16821,34 +15586,37 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int base = 10; - char_u *p; long n; int what; if (argvars[1].v_type != VAR_UNKNOWN) { - base = get_tv_number(&argvars[1]); + base = tv_get_number(&argvars[1]); if (base != 2 && base != 8 && base != 10 && base != 16) { EMSG(_(e_invarg)); return; } } - p = skipwhite(get_tv_string(&argvars[0])); + char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); if (*p == '+') { p = skipwhite(p + 1); } switch (base) { - case 2: - what = STR2NR_BIN + STR2NR_FORCE; + case 2: { + what = STR2NR_BIN | STR2NR_FORCE; break; - case 8: - what = STR2NR_OCT + STR2NR_FORCE; + } + case 8: { + what = STR2NR_OCT | STR2NR_FORCE; break; - case 16: - what = STR2NR_HEX + STR2NR_FORCE; + } + case 16: { + what = STR2NR_HEX | STR2NR_FORCE; break; - default: + } + default: { what = 0; + } } vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); rettv->vval.v_number = n; @@ -16859,17 +15627,16 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u result_buf[256]; time_t seconds; - char_u *p; rettv->v_type = VAR_STRING; - p = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) + char *p = (char *)tv_get_string(&argvars[0]); + if (argvars[1].v_type == VAR_UNKNOWN) { seconds = time(NULL); - else - seconds = (time_t)get_tv_number(&argvars[1]); + } else { + seconds = (time_t)tv_get_number(&argvars[1]); + } struct tm curtime; struct tm *curtime_ptr = os_localtime_r(&seconds, &curtime); @@ -16883,23 +15650,27 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) conv.vc_type = CONV_NONE; enc = enc_locale(); convert_setup(&conv, p_enc, enc); - if (conv.vc_type != CONV_NONE) - p = string_convert(&conv, p, NULL); - if (p != NULL) - (void)strftime((char *)result_buf, sizeof(result_buf), - (char *)p, curtime_ptr); - else + if (conv.vc_type != CONV_NONE) { + p = (char *)string_convert(&conv, (char_u *)p, NULL); + } + char result_buf[256]; + if (p != NULL) { + (void)strftime(result_buf, sizeof(result_buf), p, curtime_ptr); + } else { result_buf[0] = NUL; + } - if (conv.vc_type != CONV_NONE) + if (conv.vc_type != CONV_NONE) { xfree(p); + } convert_setup(&conv, enc, p_enc); - if (conv.vc_type != CONV_NONE) - rettv->vval.v_string = string_convert(&conv, result_buf, NULL); - else - rettv->vval.v_string = vim_strsave(result_buf); + if (conv.vc_type != CONV_NONE) { + rettv->vval.v_string = string_convert(&conv, (char_u *)result_buf, NULL); + } else { + rettv->vval.v_string = (char_u *)xstrdup(result_buf); + } - /* Release conversion descriptors */ + // Release conversion descriptors. convert_setup(&conv, NULL, NULL); xfree(enc); } @@ -16908,33 +15679,28 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "strgetchar()" function static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; - int len; - int error = false; - int charidx; - rettv->vval.v_number = -1; - str = get_tv_string_chk(&argvars[0]); + + const char *const str = tv_get_string_chk(&argvars[0]); if (str == NULL) { return; } - len = (int)STRLEN(str); - charidx = get_tv_number_chk(&argvars[1], &error); + bool error = false; + varnumber_T charidx = tv_get_number_chk(&argvars[1], &error); if (error) { return; } - { - int byteidx = 0; + const size_t len = STRLEN(str); + size_t byteidx = 0; - while (charidx >= 0 && byteidx < len) { - if (charidx == 0) { - rettv->vval.v_number = mb_ptr2char(str + byteidx); - break; - } - charidx--; - byteidx += MB_CPTR2LEN(str + byteidx); + while (charidx >= 0 && byteidx < len) { + if (charidx == 0) { + rettv->vval.v_number = mb_ptr2char((const char_u *)str + byteidx); + break; } + charidx--; + byteidx += MB_CPTR2LEN((const char_u *)str + byteidx); } } @@ -16943,32 +15709,33 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *save_haystack; - char_u *pos; - int start_idx; - - needle = get_tv_string_chk(&argvars[1]); - save_haystack = haystack = get_tv_string_buf_chk(&argvars[0], buf); rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ + + char buf[NUMBUFLEN]; + const char *const needle = tv_get_string_chk(&argvars[1]); + const char *haystack = tv_get_string_buf_chk(&argvars[0], buf); + const char *const haystack_start = haystack; + if (needle == NULL || haystack == NULL) { + return; // Type error; errmsg already given. + } if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; - start_idx = get_tv_number_chk(&argvars[2], &error); - if (error || start_idx >= (int)STRLEN(haystack)) + const ptrdiff_t start_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], + &error); + if (error || start_idx >= (ptrdiff_t)strlen(haystack)) { return; - if (start_idx >= 0) + } + if (start_idx >= 0) { haystack += start_idx; + } } - pos = (char_u *)strstr((char *)haystack, (char *)needle); - if (pos != NULL) - rettv->vval.v_number = (varnumber_T)(pos - save_haystack); + const char *pos = strstr(haystack, needle); + if (pos != NULL) { + rettv->vval.v_number = (varnumber_T)(pos - haystack_start); + } } /* @@ -16985,8 +15752,7 @@ static void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = (varnumber_T)(STRLEN( - get_tv_string(&argvars[0]))); + rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0])); } /* @@ -16994,21 +15760,21 @@ static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *s = tv_get_string(&argvars[0]); int skipcc = 0; varnumber_T len = 0; - int (*func_mb_ptr2char_adv)(char_u **pp); + int (*func_mb_ptr2char_adv)(const char_u **pp); if (argvars[1].v_type != VAR_UNKNOWN) { - skipcc = get_tv_number_chk(&argvars[1], NULL); + skipcc = tv_get_number_chk(&argvars[1], NULL); } if (skipcc < 0 || skipcc > 1) { EMSG(_(e_invarg)); } else { func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; while (*s != NUL) { - func_mb_ptr2char_adv(&s); - ++len; + func_mb_ptr2char_adv((const char_u **)&s); + len++; } rettv->vval.v_number = len; } @@ -17019,13 +15785,14 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *const s = tv_get_string(&argvars[0]); int col = 0; - if (argvars[1].v_type != VAR_UNKNOWN) - col = get_tv_number(&argvars[1]); + if (argvars[1].v_type != VAR_UNKNOWN) { + col = tv_get_number(&argvars[1]); + } - rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col); + rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); } /* @@ -17033,44 +15800,40 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *const s = tv_get_string(&argvars[0]); - rettv->vval.v_number = (varnumber_T) mb_string2cells(s); + rettv->vval.v_number = (varnumber_T)mb_string2cells((const char_u *)s); } // "strcharpart()" function -static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int nchar; - int nbyte = 0; - int charlen; - int len = 0; - int slen; - int error = false; - - p = get_tv_string(&argvars[0]); - slen = (int)STRLEN(p); +static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const char *const p = tv_get_string(&argvars[0]); + const size_t slen = STRLEN(p); - nchar = get_tv_number_chk(&argvars[1], &error); + int nbyte = 0; + bool error = false; + varnumber_T nchar = tv_get_number_chk(&argvars[1], &error); if (!error) { if (nchar > 0) { - while (nchar > 0 && nbyte < slen) { - nbyte += MB_CPTR2LEN(p + nbyte); + while (nchar > 0 && (size_t)nbyte < slen) { + nbyte += MB_CPTR2LEN((const char_u *)p + nbyte); nchar--; } } else { nbyte = nchar; } } + int len = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - charlen = get_tv_number(&argvars[2]); - while (charlen > 0 && nbyte + len < slen) { + int charlen = tv_get_number(&argvars[2]); + while (charlen > 0 && nbyte + len < (int)slen) { int off = nbyte + len; if (off < 0) { len += 1; } else { - len += MB_CPTR2LEN(p + off); + len += (size_t)MB_CPTR2LEN((const char_u *)p + off); } charlen--; } @@ -17083,17 +15846,17 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (nbyte < 0) { len += nbyte; nbyte = 0; - } else if (nbyte > slen) { + } else if ((size_t)nbyte > slen) { nbyte = slen; } if (len < 0) { len = 0; - } else if (nbyte + len > slen) { + } else if (nbyte + len > (int)slen) { len = slen - nbyte; } rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strnsave(p + nbyte, len); + rettv->vval.v_string = (char_u *)xstrndup(p + nbyte, (size_t)len); } /* @@ -17101,39 +15864,37 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { */ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int n; - int len; - int slen; - int error = FALSE; + bool error = false; - p = get_tv_string(&argvars[0]); - slen = (int)STRLEN(p); + const char *const p = tv_get_string(&argvars[0]); + const size_t slen = strlen(p); - n = get_tv_number_chk(&argvars[1], &error); - if (error) + varnumber_T n = tv_get_number_chk(&argvars[1], &error); + varnumber_T len; + if (error) { len = 0; - else if (argvars[2].v_type != VAR_UNKNOWN) - len = get_tv_number(&argvars[2]); - else - len = slen - n; /* default len: all bytes that are available. */ + } else if (argvars[2].v_type != VAR_UNKNOWN) { + len = tv_get_number(&argvars[2]); + } else { + len = slen - n; // Default len: all bytes that are available. + } - /* - * Only return the overlap between the specified part and the actual - * string. - */ + // Only return the overlap between the specified part and the actual + // string. if (n < 0) { len += n; n = 0; - } else if (n > slen) + } else if (n > (varnumber_T)slen) { n = slen; - if (len < 0) + } + if (len < 0) { len = 0; - else if (n + len > slen) + } else if (n + len > (varnumber_T)slen) { len = slen - n; + } rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strnsave(p + n, len); + rettv->vval.v_string = (char_u *)xmemdupz(p + n, (size_t)len); } /* @@ -17141,45 +15902,46 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *rest; - char_u *lastmatch = NULL; - int haystack_len, end_idx; - - needle = get_tv_string_chk(&argvars[1]); - haystack = get_tv_string_buf_chk(&argvars[0], buf); + char buf[NUMBUFLEN]; + const char *const needle = tv_get_string_chk(&argvars[1]); + const char *const haystack = tv_get_string_buf_chk(&argvars[0], buf); rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ + if (needle == NULL || haystack == NULL) { + return; // Type error; errmsg already given. + } - haystack_len = (int)STRLEN(haystack); + const size_t haystack_len = STRLEN(haystack); + ptrdiff_t end_idx; if (argvars[2].v_type != VAR_UNKNOWN) { - /* Third argument: upper limit for index */ - end_idx = get_tv_number_chk(&argvars[2], NULL); - if (end_idx < 0) - return; /* can never find a match */ - } else - end_idx = haystack_len; + // Third argument: upper limit for index. + end_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], NULL); + if (end_idx < 0) { + return; // Can never find a match. + } + } else { + end_idx = (ptrdiff_t)haystack_len; + } + const char *lastmatch = NULL; if (*needle == NUL) { - /* Empty string matches past the end. */ + // Empty string matches past the end. lastmatch = haystack + end_idx; } else { - for (rest = haystack; *rest != NUL; ++rest) { - rest = (char_u *)strstr((char *)rest, (char *)needle); - if (rest == NULL || rest > haystack + end_idx) + for (const char *rest = haystack; *rest != NUL; rest++) { + rest = strstr(rest, needle); + if (rest == NULL || rest > haystack + end_idx) { break; + } lastmatch = rest; } } - if (lastmatch == NULL) + if (lastmatch == NULL) { rettv->vval.v_number = -1; - else + } else { rettv->vval.v_number = (varnumber_T)(lastmatch - haystack); + } } /* @@ -17188,7 +15950,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = transstr(get_tv_string(&argvars[0])); + rettv->vval.v_string = transstr((char_u *)tv_get_string(&argvars[0])); } /* @@ -17196,8 +15958,8 @@ static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int error = FALSE; - int no = (int)get_tv_number_chk(&argvars[0], &error); + bool error = false; + int no = (int)tv_get_number_chk(&argvars[0], &error); if (error) { return; } @@ -17209,7 +15971,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) int retList = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - retList = get_tv_number_chk(&argvars[1], &error); + retList = tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -17229,20 +15991,20 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u patbuf[NUMBUFLEN]; - char_u subbuf[NUMBUFLEN]; - char_u flagsbuf[NUMBUFLEN]; + char patbuf[NUMBUFLEN]; + char subbuf[NUMBUFLEN]; + char flagsbuf[NUMBUFLEN]; - char_u *str = get_tv_string_chk(&argvars[0]); - char_u *pat = get_tv_string_buf_chk(&argvars[1], patbuf); - char_u *sub = NULL; - typval_T *expr = NULL; - char_u *flg = get_tv_string_buf_chk(&argvars[3], flagsbuf); + const char *const str = tv_get_string_chk(&argvars[0]); + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + const char *sub = NULL; + const char *const flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); - if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL) { + typval_T *expr = NULL; + if (tv_is_func(argvars[2])) { expr = &argvars[2]; } else { - sub = get_tv_string_buf_chk(&argvars[2], subbuf); + sub = tv_get_string_buf_chk(&argvars[2], subbuf); } rettv->v_type = VAR_STRING; @@ -17250,26 +16012,26 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) || flg == NULL) { rettv->vval.v_string = NULL; } else { - rettv->vval.v_string = do_string_sub(str, pat, sub, expr, flg); + rettv->vval.v_string = do_string_sub((char_u *)str, (char_u *)pat, + (char_u *)sub, expr, (char_u *)flg); } } /// "synID(lnum, col, trans)" function static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int id = 0; - long lnum; - long col; - int trans; - int transerr = FALSE; + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ - trans = get_tv_number_chk(&argvars[2], &transerr); + bool transerr = false; + const int trans = tv_get_number_chk(&argvars[2], &transerr); + int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count - && col >= 0 && col < (long)STRLEN(ml_get(lnum))) - id = syn_get_id(curwin, lnum, (colnr_T)col, trans, NULL, FALSE); + && col >= 0 && (size_t)col < STRLEN(ml_get(lnum))) { + id = syn_get_id(curwin, lnum, col, trans, NULL, false); + } rettv->vval.v_number = id; } @@ -17279,20 +16041,16 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = NULL; - int id; - char_u *what; - char_u *mode; - char_u modebuf[NUMBUFLEN]; + const int id = (int)tv_get_number(&argvars[0]); + const char *const what = tv_get_string(&argvars[1]); int modec; - - id = get_tv_number(&argvars[0]); - what = get_tv_string(&argvars[1]); if (argvars[2].v_type != VAR_UNKNOWN) { - mode = get_tv_string_buf(&argvars[2], modebuf); + char modebuf[NUMBUFLEN]; + const char *const mode = tv_get_string_buf(&argvars[2], modebuf); modec = TOLOWER_ASC(mode[0]); - if (modec != 'c' && modec != 'g') - modec = 0; /* replace invalid with current */ + if (modec != 'c' && modec != 'g') { + modec = 0; // Replace invalid with current. + } } else if (ui_rgb_attached()) { modec = 'g'; } else { @@ -17300,54 +16058,56 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } + const char *p = NULL; switch (TOLOWER_ASC(what[0])) { - case 'b': - if (TOLOWER_ASC(what[1]) == 'g') /* bg[#] */ + case 'b': { + if (TOLOWER_ASC(what[1]) == 'g') { // bg[#] + p = highlight_color(id, what, modec); + } else { // bold + p = highlight_has_attr(id, HL_BOLD, modec); + } + break; + } + case 'f': { // fg[#] or font p = highlight_color(id, what, modec); - else /* bold */ - p = highlight_has_attr(id, HL_BOLD, modec); - break; - - case 'f': /* fg[#] or font */ - p = highlight_color(id, what, modec); - break; - - case 'i': - if (TOLOWER_ASC(what[1]) == 'n') /* inverse */ + break; + } + case 'i': { + if (TOLOWER_ASC(what[1]) == 'n') { // inverse + p = highlight_has_attr(id, HL_INVERSE, modec); + } else { // italic + p = highlight_has_attr(id, HL_ITALIC, modec); + } + break; + } + case 'n': { // name + p = get_highlight_name(NULL, id - 1); + break; + } + case 'r': { // reverse p = highlight_has_attr(id, HL_INVERSE, modec); - else /* italic */ - p = highlight_has_attr(id, HL_ITALIC, modec); - break; - - case 'n': // name - p = (char_u *)get_highlight_name(NULL, id - 1); - break; - - case 'r': /* reverse */ - p = highlight_has_attr(id, HL_INVERSE, modec); - break; - - case 's': - if (TOLOWER_ASC(what[1]) == 'p') /* sp[#] */ - p = highlight_color(id, what, modec); - else /* standout */ - p = highlight_has_attr(id, HL_STANDOUT, modec); - break; - - case 'u': - if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') - /* underline */ - p = highlight_has_attr(id, HL_UNDERLINE, modec); - else - /* undercurl */ - p = highlight_has_attr(id, HL_UNDERCURL, modec); - break; + break; + } + case 's': { + if (TOLOWER_ASC(what[1]) == 'p') { // sp[#] + p = highlight_color(id, what, modec); + } else { // standout + p = highlight_has_attr(id, HL_STANDOUT, modec); + } + break; + } + case 'u': { + if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') { // underline + p = highlight_has_attr(id, HL_UNDERLINE, modec); + } else { // undercurl + p = highlight_has_attr(id, HL_UNDERCURL, modec); + } + break; + } } - if (p != NULL) - p = vim_strsave(p); rettv->v_type = VAR_STRING; - rettv->vval.v_string = p; + rettv->vval.v_string = (char_u *)(p == NULL ? p : xstrdup(p)); } /* @@ -17355,14 +16115,13 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int id; + int id = tv_get_number(&argvars[0]); - id = get_tv_number(&argvars[0]); - - if (id > 0) + if (id > 0) { id = syn_get_final_id(id); - else + } else { id = 0; + } rettv->vval.v_number = id; } @@ -17372,8 +16131,6 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long lnum; - long col; int syntax_flags = 0; int cchar; int matchid = 0; @@ -17382,15 +16139,16 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; memset(str, NUL, sizeof(str)); - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && col <= (long)STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { - (void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE); + && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { + (void)syn_get_id(curwin, lnum, col, false, NULL, false); syntax_flags = get_syntax_info(&matchid); // get the conceal character @@ -17408,10 +16166,10 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); + tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); // -1 to auto-determine strlen - list_append_string(rettv->vval.v_list, str, -1); - list_append_number(rettv->vval.v_list, matchid); + tv_list_append_string(rettv->vval.v_list, (const char *)str, -1); + tv_list_append_number(rettv->vval.v_list, matchid); } /* @@ -17419,56 +16177,35 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long lnum; - long col; - rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && col <= (long)STRLEN(ml_get(lnum))) { - rettv_list_alloc(rettv); - (void)syn_get_id(curwin, lnum, (colnr_T)col, FALSE, NULL, TRUE); + && (size_t)col <= STRLEN(ml_get(lnum))) { + tv_list_alloc_ret(rettv); + (void)syn_get_id(curwin, lnum, col, false, NULL, true); int id; int i = 0; while ((id = syn_get_stack_item(i++)) >= 0) { - list_append_number(rettv->vval.v_list, id); + tv_list_append_number(rettv->vval.v_list, id); } } } -static list_T* string_to_list(char_u *str, size_t len, bool keepempty) +static list_T *string_to_list(const char *str, size_t len, const bool keepempty) { - list_T *list = list_alloc(); - - // Copy each line to a list element using NL as the delimiter. - for (size_t i = 0; i < len; i++) { - char_u *start = str + i; - size_t line_len = (char_u *) xmemscan(start, NL, len - i) - start; - i += line_len; - - // Don't use a str function to copy res as it may contains NULs. - char_u *s = xmemdupz(start, line_len); - memchrsub(s, NUL, NL, line_len); // Replace NUL with NL to avoid truncation - - listitem_T *li = listitem_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = s; - list_append(list, li); - } - - // Optionally retain final newline, if present - if (keepempty && str[len-1] == NL) { - list_append_string(list, (char_u*)"", 0); + if (!keepempty && str[len - 1] == NL) { + len--; } - + list_T *const list = tv_list_alloc(); + encode_list_write(list, str, len); return list; } @@ -17483,8 +16220,8 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, } // get input to the shell command (if any), and its length - ssize_t input_len; - char *input = (char *) save_tv_as_string(&argvars[1], &input_len, false); + ptrdiff_t input_len; + char *input = save_tv_as_string(&argvars[1], &input_len, false); if (input_len < 0) { assert(input == NULL); return; @@ -17513,7 +16250,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (res == NULL) { if (retlist) { // return an empty list when there's no output - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } else { rettv->vval.v_string = (char_u *) xstrdup(""); } @@ -17523,9 +16260,9 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (retlist) { int keepempty = 0; if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { - keepempty = get_tv_number(&argvars[2]); + keepempty = tv_get_number(&argvars[2]); } - rettv->vval.v_list = string_to_list((char_u *) res, nread, keepempty != 0); + rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); rettv->vval.v_list->lv_refcount++; rettv->v_type = VAR_LIST; @@ -17568,20 +16305,20 @@ static void f_systemlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tabpage_T *tp; win_T *wp = NULL; - if (argvars[0].v_type == VAR_UNKNOWN) + if (argvars[0].v_type == VAR_UNKNOWN) { wp = firstwin; - else { - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp != NULL) + } else { + tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp != NULL) { wp = (tp == curtab) ? firstwin : tp->tp_firstwin; + } } if (wp != NULL) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); while (wp != NULL) { - list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); + tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); wp = wp->w_next; } } @@ -17594,19 +16331,20 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int nr = 1; - char_u *arg; if (argvars[0].v_type != VAR_UNKNOWN) { - arg = get_tv_string_chk(&argvars[0]); + const char *const arg = tv_get_string_chk(&argvars[0]); nr = 0; if (arg != NULL) { - if (STRCMP(arg, "$") == 0) + if (strcmp(arg, "$") == 0) { nr = tabpage_index(NULL) - 1; - else + } else { EMSG2(_(e_invexpr2), arg); + } } - } else + } else { nr = tabpage_index(curtab); + } rettv->vval.v_number = nr; } @@ -17620,19 +16358,19 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) win_T *twin; int nr = 1; win_T *wp; - char_u *arg; twin = (tp == curtab) ? curwin : tp->tp_curwin; if (argvar->v_type != VAR_UNKNOWN) { - arg = get_tv_string_chk(argvar); - if (arg == NULL) - nr = 0; /* type error; errmsg already given */ - else if (STRCMP(arg, "$") == 0) + const char *const arg = tv_get_string_chk(argvar); + if (arg == NULL) { + nr = 0; // Type error; errmsg already given. + } else if (strcmp(arg, "$") == 0) { twin = (tp == curtab) ? lastwin : tp->tp_lastwin; - else if (STRCMP(arg, "#") == 0) { + } else if (strcmp(arg, "#") == 0) { twin = (tp == curtab) ? prevwin : tp->tp_prevwin; - if (twin == NULL) + if (twin == NULL) { nr = 0; + } } else { EMSG2(_(e_invexpr2), arg); nr = 0; @@ -17658,13 +16396,12 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int nr = 1; - tabpage_T *tp; - - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp == NULL) + tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp == NULL) { nr = 0; - else + } else { nr = get_winnr(tp, &argvars[1]); + } rettv->vval.v_number = nr; } @@ -17674,16 +16411,16 @@ static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; + char *fname; tagname_T tn; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); fname = xmalloc(MAXPATHL); - int first = TRUE; - while (get_tagfname(&tn, first, fname) == OK) { - list_append_string(rettv->vval.v_list, fname, -1); - first = FALSE; + bool first = true; + while (get_tagfname(&tn, first, (char_u *)fname) == OK) { + tv_list_append_string(rettv->vval.v_list, fname, -1); + first = false; } tagname_free(&tn); @@ -17695,15 +16432,14 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *tag_pattern; + const char *const tag_pattern = tv_get_string(&argvars[0]); - tag_pattern = get_tv_string(&argvars[0]); - - rettv->vval.v_number = FALSE; - if (*tag_pattern == NUL) + rettv->vval.v_number = false; + if (*tag_pattern == NUL) { return; + } - (void)get_tags(rettv_list_alloc(rettv), tag_pattern); + (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern); } /* @@ -17727,7 +16463,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - char *cmd; + const char *cmd; bool executable = true; char **argv = tv_to_argv(&argvars[0], &cmd, &executable); if (!argv) { @@ -17745,15 +16481,15 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; - char *cwd = "."; + const char *cwd = "."; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; - char *new_cwd = (char *)get_dict_string(job_opts, "cwd", false); - if (new_cwd && strlen(new_cwd) > 0) { + const char *const new_cwd = tv_dict_get_string(job_opts, "cwd", false); + if (new_cwd && *new_cwd != NUL) { cwd = new_cwd; // The new cwd must be a directory. - if (!os_isdir((char_u *)cwd)) { + if (!os_isdir((const char_u *)cwd)) { EMSG2(_(e_invarg2), "expected valid directory"); shell_free_argv(argv); return; @@ -17790,7 +16526,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) // at this point the buffer has no terminal instance associated yet, so unset // the 'swapfile' option to ensure no swap file will be created curbuf->b_p_swf = false; - (void)setfname(curbuf, (uint8_t *)buf, NULL, true); + (void)setfname(curbuf, (char_u *)buf, NULL, true); // Save the job id and pid in b:terminal_job_{id,pid} Error err; dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), @@ -17814,7 +16550,8 @@ static void f_test_garbagecollect_now(typval_T *argvars, garbage_collect(true); } -static bool callback_from_typval(Callback *callback, typval_T *arg) +bool callback_from_typval(Callback *const callback, typval_T *const arg) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) { callback->data.partial = arg->vval.v_partial; @@ -17834,53 +16571,33 @@ static bool callback_from_typval(Callback *callback, typval_T *arg) return true; } - /// Unref/free callback -static void callback_free(Callback *callback) +void callback_free(Callback *const callback) + FUNC_ATTR_NONNULL_ALL { switch (callback->type) { - case kCallbackFuncref: + case kCallbackFuncref: { func_unref(callback->data.funcref); xfree(callback->data.funcref); break; - - case kCallbackPartial: + } + case kCallbackPartial: { partial_unref(callback->data.partial); break; - - case kCallbackNone: + } + case kCallbackNone: { break; - - default: + } + default: { abort(); + } } callback->type = kCallbackNone; } -static bool callback_equal(Callback *cb1, Callback *cb2) -{ - if (cb1->type != cb2->type) { - return false; - } - switch (cb1->type) { - case kCallbackFuncref: - return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0; - - case kCallbackPartial: - // FIXME: this is inconsistent with tv_equal but is needed for precision - // maybe change dictwatcheradd to return a watcher id instead? - return cb1->data.partial == cb2->data.partial; - - case kCallbackNone: - return true; - - default: - abort(); - } -} - -static bool callback_call(Callback *callback, int argcount_in, - typval_T *argvars_in, typval_T *rettv) +bool callback_call(Callback *const callback, const int argcount_in, + typval_T *const argvars_in, typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL { partial_T *partial; char_u *name; @@ -17935,19 +16652,18 @@ static bool set_ref_in_callback(Callback *callback, int copyID, static void add_timer_info(typval_T *rettv, timer_T *timer) { list_T *list = rettv->vval.v_list; - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); - list_append_dict(list, dict); - dict_add_nr_str(dict, "id", (long)timer->timer_id, NULL); - dict_add_nr_str(dict, "time", timer->timeout, NULL); - dict_add_nr_str(dict, "paused", (long)timer->paused, NULL); + tv_list_append_dict(list, dict); + tv_dict_add_nr(dict, S_LEN("id"), timer->timer_id); + tv_dict_add_nr(dict, S_LEN("time"), timer->timeout); + tv_dict_add_nr(dict, S_LEN("paused"), timer->paused); - dict_add_nr_str(dict, "repeat", - (long)(timer->repeat_count < 0 ? -1 : timer->repeat_count), - NULL); + tv_dict_add_nr(dict, S_LEN("repeat"), + (timer->repeat_count < 0 ? -1 : timer->repeat_count)); - dictitem_T *di = dictitem_alloc((char_u *)"callback"); - if (dict_add(dict, di) == FAIL) { + dictitem_T *di = tv_dict_item_alloc("callback"); + if (tv_dict_add(dict, di) == FAIL) { xfree(di); return; } @@ -17976,12 +16692,12 @@ static void add_timer_info_all(typval_T *rettv) /// "timer_info([timer])" function static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type != VAR_UNKNOWN) { if (argvars[0].v_type != VAR_NUMBER) { EMSG(_(e_number_exp)); } else { - timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0])); + timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0])); if (timer != NULL && !timer->stopped) { add_timer_info(rettv, timer); } @@ -17997,8 +16713,8 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr) if (argvars[0].v_type != VAR_NUMBER) { EMSG(_(e_number_exp)); } else { - int paused = (bool)get_tv_number(&argvars[1]); - timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0])); + int paused = (bool)tv_get_number(&argvars[1]); + timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0])); if (timer != NULL) { timer->paused = paused; } @@ -18008,7 +16724,7 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr) /// "timer_start(timeout, callback, opts)" function static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long timeout = get_tv_number(&argvars[0]); + const long timeout = tv_get_number(&argvars[0]); timer_T *timer; int repeat = 1; dict_T *dict; @@ -18018,11 +16734,12 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_DICT || (dict = argvars[2].vval.v_dict) == NULL) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[2])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[2])); return; } - if (dict_find(dict, (char_u *)"repeat", -1) != NULL) { - repeat = get_dict_number(dict, "repeat"); + dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); + if (di != NULL) { + repeat = tv_get_number(&di->di_tv); if (repeat == 0) { repeat = 1; } @@ -18062,7 +16779,7 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0])); + timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0])); if (timer == NULL) { return; @@ -18090,15 +16807,13 @@ static void timer_due_cb(TimeWatcher *tw, void *data) timer_stop(timer); } - typval_T argv[2]; - init_tv(argv); + typval_T argv[2] = { TV_INITIAL_VALUE, TV_INITIAL_VALUE }; argv[0].v_type = VAR_NUMBER; argv[0].vval.v_number = timer->timer_id; - typval_T rettv; + typval_T rettv = TV_INITIAL_VALUE; - init_tv(&rettv); callback_call(&timer->callback, 1, argv, &rettv); - clear_tv(&rettv); + tv_clear(&rettv); if (!timer->stopped && timer->timeout == 0) { // special case: timeout=0 means the callback will be @@ -18151,7 +16866,7 @@ void timer_teardown(void) */ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = vim_strsave(get_tv_string(&argvars[0])); + char_u *p = (char_u *)xstrdup(tv_get_string(&argvars[0])); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; @@ -18183,7 +16898,7 @@ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = strup_save(get_tv_string(&argvars[0])); + rettv->vval.v_string = (char_u *)strup_save(tv_get_string(&argvars[0])); } /* @@ -18191,77 +16906,71 @@ static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *in_str; - char_u *fromstr; - char_u *tostr; - char_u *p; - int inlen; - int fromlen; - int tolen; - int idx; - char_u *cpstr; - int cplen; - int first = TRUE; - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - garray_T ga; + char buf[NUMBUFLEN]; + char buf2[NUMBUFLEN]; - in_str = get_tv_string(&argvars[0]); - fromstr = get_tv_string_buf_chk(&argvars[1], buf); - tostr = get_tv_string_buf_chk(&argvars[2], buf2); + const char *in_str = tv_get_string(&argvars[0]); + const char *fromstr = tv_get_string_buf_chk(&argvars[1], buf); + const char *tostr = tv_get_string_buf_chk(&argvars[2], buf2); - /* Default return value: empty string. */ + // Default return value: empty string. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - if (fromstr == NULL || tostr == NULL) - return; /* type error; errmsg already given */ + if (fromstr == NULL || tostr == NULL) { + return; // Type error; errmsg already given. + } + garray_T ga; ga_init(&ga, (int)sizeof(char), 80); - if (!has_mbyte) - /* not multi-byte: fromstr and tostr must be the same length */ - if (STRLEN(fromstr) != STRLEN(tostr)) { -error: - EMSG2(_(e_invarg2), fromstr); - ga_clear(&ga); - return; + if (!has_mbyte) { + // Not multi-byte: fromstr and tostr must be the same length. + if (strlen(fromstr) != strlen(tostr)) { + goto error; } + } - /* fromstr and tostr have to contain the same number of chars */ + // fromstr and tostr have to contain the same number of chars. + bool first = true; while (*in_str != NUL) { if (has_mbyte) { - inlen = (*mb_ptr2len)(in_str); - cpstr = in_str; - cplen = inlen; - idx = 0; - for (p = fromstr; *p != NUL; p += fromlen) { - fromlen = (*mb_ptr2len)(p); + const char *cpstr = in_str; + const int inlen = (*mb_ptr2len)((const char_u *)in_str); + int cplen = inlen; + int idx = 0; + int fromlen; + for (const char *p = fromstr; *p != NUL; p += fromlen) { + fromlen = (*mb_ptr2len)((const char_u *)p); if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) { + int tolen; for (p = tostr; *p != NUL; p += tolen) { - tolen = (*mb_ptr2len)(p); + tolen = (*mb_ptr2len)((const char_u *)p); if (idx-- == 0) { cplen = tolen; - cpstr = p; + cpstr = (char *)p; break; } } - if (*p == NUL) /* tostr is shorter than fromstr */ + if (*p == NUL) { // tostr is shorter than fromstr. goto error; + } break; } - ++idx; + idx++; } if (first && cpstr == in_str) { - /* Check that fromstr and tostr have the same number of - * (multi-byte) characters. Done only once when a character - * of in_str doesn't appear in fromstr. */ - first = FALSE; - for (p = tostr; *p != NUL; p += tolen) { - tolen = (*mb_ptr2len)(p); - --idx; + // Check that fromstr and tostr have the same number of + // (multi-byte) characters. Done only once when a character + // of in_str doesn't appear in fromstr. + first = false; + int tolen; + for (const char *p = tostr; *p != NUL; p += tolen) { + tolen = (*mb_ptr2len)((const char_u *)p); + idx--; } - if (idx != 0) + if (idx != 0) { goto error; + } } ga_grow(&ga, cplen); @@ -18270,13 +16979,14 @@ error: in_str += inlen; } else { - /* When not using multi-byte chars we can do it faster. */ - p = vim_strchr(fromstr, *in_str); - if (p != NULL) + // When not using multi-byte chars we can do it faster. + const char *const p = strchr(fromstr, *in_str); + if (p != NULL) { ga_append(&ga, tostr[p - fromstr]); - else + } else { ga_append(&ga, *in_str); - ++in_str; + } + in_str++; } } @@ -18284,6 +16994,11 @@ error: ga_append(&ga, NUL); rettv->vval.v_string = ga.ga_data; + return; +error: + EMSG2(_(e_invarg2), fromstr); + ga_clear(&ga); + return; } /* @@ -18329,20 +17044,18 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - { - char_u *fname = get_tv_string(&argvars[0]); + const char *const fname = tv_get_string(&argvars[0]); - if (*fname == NUL) { - /* If there is no file name there will be no undo file. */ - rettv->vval.v_string = NULL; - } else { - char *ffname = FullName_save((char *)fname, false); + if (*fname == NUL) { + // If there is no file name there will be no undo file. + rettv->vval.v_string = NULL; + } else { + char *ffname = FullName_save(fname, false); - if (ffname != NULL) { - rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false); - } - xfree(ffname); + if (ffname != NULL) { + rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false); } + xfree(ffname); } } @@ -18351,22 +17064,22 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; list_T *list; - dict_add_nr_str(dict, "synced", (long)curbuf->b_u_synced, NULL); - dict_add_nr_str(dict, "seq_last", curbuf->b_u_seq_last, NULL); - dict_add_nr_str(dict, "save_last", - (long)curbuf->b_u_save_nr_last, NULL); - dict_add_nr_str(dict, "seq_cur", curbuf->b_u_seq_cur, NULL); - dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL); - dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL); + tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced); + tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last); + tv_dict_add_nr(dict, S_LEN("save_last"), + (varnumber_T)curbuf->b_u_save_nr_last); + tv_dict_add_nr(dict, S_LEN("seq_cur"), (varnumber_T)curbuf->b_u_seq_cur); + tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur); + tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur); - list = list_alloc(); + list = tv_list_alloc(); u_eval_tree(curbuf->b_u_oldhead, list); - dict_add_list(dict, "entries", list); + tv_dict_add_list(dict, S_LEN("entries"), list); } /* @@ -18425,7 +17138,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_findbuf()" function static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); win_findbuf(argvars, rettv->vval.v_list); } @@ -18444,7 +17157,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_id2tabwin()" function static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); win_id2tabwin(argvars, rettv->vval.v_list); } @@ -18539,36 +17252,37 @@ static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - dict_T *dict; + dict_T *dict; if (argvars[0].v_type != VAR_DICT - || (dict = argvars[0].vval.v_dict) == NULL) - EMSG(_(e_invarg)); - else { - if (dict_find(dict, (char_u *)"lnum", -1) != NULL) { - curwin->w_cursor.lnum = get_dict_number(dict, "lnum"); + || (dict = argvars[0].vval.v_dict) == NULL) { + emsgf(_(e_invarg)); + } else { + dictitem_T *di; + if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { + curwin->w_cursor.lnum = tv_get_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"col", -1) != NULL) { - curwin->w_cursor.col = get_dict_number(dict, "col"); + if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { + curwin->w_cursor.col = tv_get_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"coladd", -1) != NULL) { - curwin->w_cursor.coladd = get_dict_number(dict, "coladd"); + if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { + curwin->w_cursor.coladd = tv_get_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"curswant", -1) != NULL) { - curwin->w_curswant = get_dict_number(dict, "curswant"); + if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { + curwin->w_curswant = tv_get_number(&di->di_tv); curwin->w_set_curswant = false; } - if (dict_find(dict, (char_u *)"topline", -1) != NULL) { - set_topline(curwin, get_dict_number(dict, "topline")); + if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { + set_topline(curwin, tv_get_number(&di->di_tv)); } - if (dict_find(dict, (char_u *)"topfill", -1) != NULL) { - curwin->w_topfill = get_dict_number(dict, "topfill"); + if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { + curwin->w_topfill = tv_get_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"leftcol", -1) != NULL) { - curwin->w_leftcol = get_dict_number(dict, "leftcol"); + if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { + curwin->w_leftcol = tv_get_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"skipcol", -1) != NULL) { - curwin->w_skipcol = get_dict_number(dict, "skipcol"); + if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { + curwin->w_skipcol = tv_get_number(&di->di_tv); } check_cursor(); @@ -18591,19 +17305,19 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *dict; - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL); - dict_add_nr_str(dict, "col", (long)curwin->w_cursor.col, NULL); - dict_add_nr_str(dict, "coladd", (long)curwin->w_cursor.coladd, NULL); + tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum); + tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col); + tv_dict_add_nr(dict, S_LEN("coladd"), (varnumber_T)curwin->w_cursor.coladd); update_curswant(); - dict_add_nr_str(dict, "curswant", (long)curwin->w_curswant, NULL); + tv_dict_add_nr(dict, S_LEN("curswant"), (varnumber_T)curwin->w_curswant); - dict_add_nr_str(dict, "topline", (long)curwin->w_topline, NULL); - dict_add_nr_str(dict, "topfill", (long)curwin->w_topfill, NULL); - dict_add_nr_str(dict, "leftcol", (long)curwin->w_leftcol, NULL); - dict_add_nr_str(dict, "skipcol", (long)curwin->w_skipcol, NULL); + tv_dict_add_nr(dict, S_LEN("topline"), (varnumber_T)curwin->w_topline); + tv_dict_add_nr(dict, S_LEN("topfill"), (varnumber_T)curwin->w_topfill); + tv_dict_add_nr(dict, S_LEN("leftcol"), (varnumber_T)curwin->w_leftcol); + tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol); } /// Writes list of strings to file @@ -18618,8 +17332,7 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list, { int error = 0; for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - const char *const s = (const char *)get_tv_string_chk( - (typval_T *)&li->li_tv); + const char *const s = tv_get_string_chk(&li->li_tv); if (s == NULL) { return false; } @@ -18654,7 +17367,7 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list, } } } - if ((error = file_fsync(fp)) != 0) { + if ((error = file_flush(fp)) != 0) { goto write_list_error; } return true; @@ -18700,7 +17413,7 @@ void init_static_list(staticList10_T *sl) /// @param[in] endnl If true, the output will end in a newline (if a list). /// @returns an allocated string if `tv` represents a VimL string, list, or /// number; NULL otherwise. -static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl) +static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { if (tv->v_type == VAR_UNKNOWN) { @@ -18708,34 +17421,33 @@ static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl) return NULL; } - // For types other than list, let get_tv_string_buf_chk() get the value or + // For types other than list, let tv_get_string_buf_chk() get the value or // print an error. if (tv->v_type != VAR_LIST) { - char_u *ret = get_tv_string_chk(tv); - if (ret && (*len = STRLEN(ret))) { - ret = vim_strsave(ret); + const char *ret = tv_get_string_chk(tv); + if (ret && (*len = strlen(ret))) { + return xmemdupz(ret, (size_t)(*len)); } else { - ret = NULL; *len = -1; + return NULL; } - return ret; } // Pre-calculate the resulting length. *len = 0; list_T *list = tv->vval.v_list; for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - *len += STRLEN(get_tv_string(&li->li_tv)) + 1; + *len += strlen(tv_get_string(&li->li_tv)) + 1; } if (*len == 0) { return NULL; } - char_u *ret = xmalloc(*len + endnl); - char_u *end = ret; + char *ret = xmalloc(*len + endnl); + char *end = ret; for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - for (char_u *s = get_tv_string(&li->li_tv); *s != NUL; s++) { + for (const char *s = tv_get_string(&li->li_tv); *s != NUL; s++) { *end++ = (*s == '\n') ? NUL : *s; } if (endnl || li->li_next != NULL) { @@ -18764,7 +17476,7 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "wordcount()" function static void f_wordcount(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); cursor_pos_info(rettv->vval.v_dict); } @@ -18787,40 +17499,47 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool binary = false; bool append = false; + bool do_fsync = !!p_fs; if (argvars[2].v_type != VAR_UNKNOWN) { - const char *const flags = (const char *)get_tv_string_chk(&argvars[2]); + const char *const flags = tv_get_string_chk(&argvars[2]); if (flags == NULL) { return; } - if (strchr(flags, 'b')) { - binary = true; - } - if (strchr(flags, 'a')) { - append = true; + for (const char *p = flags; *p; p++) { + switch (*p) { + case 'b': { binary = true; break; } + case 'a': { append = true; break; } + case 's': { do_fsync = true; break; } + case 'S': { do_fsync = false; break; } + default: { + // Using %s, p and not %c, *p to preserve multibyte characters + emsgf(_("E5060: Unknown flag: %s"), p); + return; + } + } } } - const char buf[NUMBUFLEN]; - const char *const fname = (const char *)get_tv_string_buf_chk(&argvars[1], - (char_u *)buf); + char buf[NUMBUFLEN]; + const char *const fname = tv_get_string_buf_chk(&argvars[1], buf); if (fname == NULL) { return; } - FileDescriptor *fp; + FileDescriptor fp; int error; rettv->vval.v_number = -1; if (*fname == NUL) { EMSG(_("E482: Can't open file with an empty name")); - } else if ((fp = file_open_new(&error, fname, - ((append ? kFileAppend : kFileTruncate) - | kFileCreate), 0666)) == NULL) { + } else if ((error = file_open(&fp, fname, + ((append ? kFileAppend : kFileTruncate) + | kFileCreate), 0666)) != 0) { emsgf(_("E482: Can't open file %s for writing: %s"), fname, os_strerror(error)); } else { - if (write_list(fp, argvars[0].vval.v_list, binary)) { + if (write_list(&fp, argvars[0].vval.v_list, binary)) { rettv->vval.v_number = 0; } - if ((error = file_free(fp)) != 0) { + if ((error = file_close(&fp, do_fsync)) != 0) { emsgf(_("E80: Error when closing file %s: %s"), fname, os_strerror(error)); } @@ -18831,82 +17550,96 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - ^ get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + ^ tv_get_number_chk(&argvars[1], NULL); } -/* - * Translate a String variable into a position. - * Returns NULL when there is an error. - */ -static pos_T * -var2fpos ( - typval_T *varp, - int dollar_lnum, /* TRUE when $ is last line */ - int *fnum /* set to fnum for '0, 'A, etc. */ -) +/// Translate a VimL object into a position +/// +/// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid +/// type. +/// +/// @param[in] tv Object to translate. +/// @param[in] dollar_lnum True when "$" is last line. +/// @param[out] ret_fnum Set to fnum for marks. +/// +/// @return Pointer to position or NULL in case of error (e.g. invalid type). +pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, + int *const ret_fnum) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char_u *name; static pos_T pos; pos_T *pp; - /* Argument can be [lnum, col, coladd]. */ - if (varp->v_type == VAR_LIST) { + // Argument can be [lnum, col, coladd]. + if (tv->v_type == VAR_LIST) { list_T *l; int len; - int error = FALSE; - listitem_T *li; + bool error = false; + listitem_T *li; - l = varp->vval.v_list; - if (l == NULL) + l = tv->vval.v_list; + if (l == NULL) { return NULL; + } - /* Get the line number */ - pos.lnum = list_find_nr(l, 0L, &error); - if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) - return NULL; /* invalid line number */ + // Get the line number. + pos.lnum = tv_list_find_nr(l, 0L, &error); + if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) { + // Invalid line number. + return NULL; + } - /* Get the column number */ - pos.col = list_find_nr(l, 1L, &error); - if (error) + // Get the column number. + pos.col = tv_list_find_nr(l, 1L, &error); + if (error) { return NULL; + } len = (long)STRLEN(ml_get(pos.lnum)); - /* We accept "$" for the column number: last column. */ - li = list_find(l, 1L); + // We accept "$" for the column number: last column. + li = tv_list_find(l, 1L); if (li != NULL && li->li_tv.v_type == VAR_STRING && li->li_tv.vval.v_string != NULL - && STRCMP(li->li_tv.vval.v_string, "$") == 0) + && STRCMP(li->li_tv.vval.v_string, "$") == 0) { pos.col = len + 1; + } - /* Accept a position up to the NUL after the line. */ - if (pos.col == 0 || (int)pos.col > len + 1) - return NULL; /* invalid column number */ - --pos.col; + // Accept a position up to the NUL after the line. + if (pos.col == 0 || (int)pos.col > len + 1) { + // Invalid column number. + return NULL; + } + pos.col--; - /* Get the virtual offset. Defaults to zero. */ - pos.coladd = list_find_nr(l, 2L, &error); - if (error) + // Get the virtual offset. Defaults to zero. + pos.coladd = tv_list_find_nr(l, 2L, &error); + if (error) { pos.coladd = 0; + } return &pos; } - name = get_tv_string_chk(varp); - if (name == NULL) + const char *const name = tv_get_string_chk(tv); + if (name == NULL) { return NULL; - if (name[0] == '.') /* cursor */ + } + if (name[0] == '.') { // Cursor. return &curwin->w_cursor; - if (name[0] == 'v' && name[1] == NUL) { /* Visual start */ - if (VIsual_active) + } + if (name[0] == 'v' && name[1] == NUL) { // Visual start. + if (VIsual_active) { return &VIsual; + } return &curwin->w_cursor; } - if (name[0] == '\'') { /* mark */ - pp = getmark_buf_fnum(curbuf, name[1], FALSE, fnum); - if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) + if (name[0] == '\'') { // Mark. + pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum); + if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) { return NULL; + } return pp; } @@ -18959,32 +17692,37 @@ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) return FAIL; if (fnump != NULL) { - n = list_find_nr(l, i++, NULL); /* fnum */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // fnum + if (n < 0) { return FAIL; - if (n == 0) - n = curbuf->b_fnum; /* current buffer */ + } + if (n == 0) { + n = curbuf->b_fnum; // Current buffer. + } *fnump = n; } - n = list_find_nr(l, i++, NULL); /* lnum */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // lnum + if (n < 0) { return FAIL; + } posp->lnum = n; - n = list_find_nr(l, i++, NULL); /* col */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // col + if (n < 0) { return FAIL; + } posp->col = n; - n = list_find_nr(l, i, NULL); // off - if (n < 0) + n = tv_list_find_nr(l, i, NULL); // off + if (n < 0) { posp->coladd = 0; - else + } else { posp->coladd = n; + } if (curswantp != NULL) { - *curswantp = list_find_nr(l, i + 1, NULL); // curswant + *curswantp = tv_list_find_nr(l, i + 1, NULL); // curswant } return OK; @@ -18995,15 +17733,16 @@ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) * Advance "arg" to the first character after the name. * Return 0 for error. */ -static int get_env_len(char_u **arg) +static int get_env_len(const char_u **arg) { - char_u *p; int len; - for (p = *arg; vim_isIDc(*p); ++p) - ; - if (p == *arg) /* no name found */ + const char_u *p; + for (p = *arg; vim_isIDc(*p); p++) { + } + if (p == *arg) { // No name found. return 0; + } len = (int)(p - *arg); *arg = p; @@ -19055,8 +17794,6 @@ static int get_name_len(const char **const arg, int verbose) { int len; - char_u *expr_start; - char_u *expr_end; *alias = NULL; /* default to no alias */ @@ -19072,12 +17809,12 @@ static int get_name_len(const char **const arg, *arg += len; } - /* - * Find the end of the name; check for {} construction. - */ + // Find the end of the name; check for {} construction. + char_u *expr_start; + char_u *expr_end; const char *p = (const char *)find_name_end((char_u *)(*arg), - &expr_start, - &expr_end, + (const char_u **)&expr_start, + (const char_u **)&expr_end, len > 0 ? 0 : FNE_CHECK_START); if (expr_start != NULL) { if (!evaluate) { @@ -19113,12 +17850,11 @@ static int get_name_len(const char **const arg, // "flags" can have FNE_INCL_BR and FNE_CHECK_START. // Return a pointer to just after the name. Equal to "arg" if there is no // valid name. -static char_u *find_name_end(char_u *arg, char_u **expr_start, - char_u **expr_end, int flags) +static const char_u *find_name_end(const char_u *arg, const char_u **expr_start, + const char_u **expr_end, int flags) { int mb_nest = 0; int br_nest = 0; - char_u *p; int len; if (expr_start != NULL) { @@ -19131,6 +17867,7 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, return arg; } + const char_u *p; for (p = arg; *p != NUL && (eval_isnamec(*p) || *p == '{' @@ -19202,7 +17939,8 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, * Returns a new allocated string, which the caller must free. * Returns NULL for failure. */ -static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end) +static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, + char_u *expr_end, char_u *in_end) { char_u c1; char_u *retval = NULL; @@ -19231,7 +17969,9 @@ static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u * *expr_end = '}'; if (retval != NULL) { - temp_result = find_name_end(retval, &expr_start, &expr_end, 0); + temp_result = (char_u *)find_name_end(retval, + (const char_u **)&expr_start, + (const char_u **)&expr_end, 0); if (expr_start != NULL) { /* Further expansion! */ temp_result = make_expanded_name(retval, expr_start, @@ -19275,7 +18015,7 @@ long get_vim_var_nr(int idx) FUNC_ATTR_PURE */ char_u *get_vim_var_str(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET { - return get_tv_string(&vimvars[idx].vv_tv); + return (char_u *)tv_get_string(&vimvars[idx].vv_tv); } /* @@ -19328,7 +18068,7 @@ void set_vcount(long count, long count1, int set_prevcount) /// @param[in] val Value to set to. void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val) { - clear_tv(&vimvars[idx].vv_tv); + tv_clear(&vimvars[idx].vv_tv); vimvars[idx].vv_type = VAR_NUMBER; vimvars[idx].vv_nr = val; } @@ -19339,7 +18079,7 @@ void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val) /// @param[in] val Value to set to. void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) { - clear_tv(&vimvars[idx].vv_tv); + tv_clear(&vimvars[idx].vv_tv); vimvars[idx].vv_type = VAR_SPECIAL; vimvars[idx].vv_special = val; } @@ -19353,7 +18093,7 @@ void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) void set_vim_var_string(const VimVarIndex idx, const char *const val, const ptrdiff_t len) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_STRING; if (val == NULL) { vimvars[idx].vv_str = NULL; @@ -19370,7 +18110,7 @@ void set_vim_var_string(const VimVarIndex idx, const char *const val, /// @param[in,out] val Value to set to. Reference count will be incremented. void set_vim_var_list(const VimVarIndex idx, list_T *const val) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_LIST; vimvars[idx].vv_list = val; if (val != NULL) { @@ -19385,14 +18125,14 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val) /// Also keys of the dictionary will be made read-only. void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_DICT; vimvars[idx].vv_dict = val; if (val != NULL) { val->dv_refcount++; // Set readonly - dict_set_keys_readonly(val); + tv_dict_set_keys_readonly(val); } } @@ -19537,7 +18277,7 @@ static int get_var_tv( } ret = FAIL; } else if (rettv != NULL) { - copy_tv(tv, rettv); + tv_copy(tv, rettv); } return ret; @@ -19581,8 +18321,7 @@ handle_subscript( while (ret == OK && (**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) - || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL))) + || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) && !ascii_iswhite(*(*arg - 1))) { if (**arg == '(') { partial_T *pt = NULL; @@ -19605,23 +18344,25 @@ handle_subscript( curwin->w_cursor.lnum, curwin->w_cursor.lnum, &len, evaluate, pt, selfdict); - /* Clear the funcref afterwards, so that deleting it while - * evaluating the arguments is possible (see test55). */ - if (evaluate) - clear_tv(&functv); + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&functv); + } /* Stop the expression evaluation when immediately aborting on * error, or when an interrupt occurred or an exception was thrown * but not caught. */ if (aborting()) { - if (ret == OK) - clear_tv(rettv); + if (ret == OK) { + tv_clear(rettv); + } ret = FAIL; } - dict_unref(selfdict); + tv_dict_unref(selfdict); selfdict = NULL; - } else { /* **arg == '[' || **arg == '.' */ - dict_unref(selfdict); + } else { // **arg == '[' || **arg == '.' + tv_dict_unref(selfdict); if (rettv->v_type == VAR_DICT) { selfdict = rettv->vval.v_dict; if (selfdict != NULL) @@ -19629,24 +18370,22 @@ handle_subscript( } else selfdict = NULL; if (eval_index((char_u **)arg, rettv, evaluate, verbose) == FAIL) { - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } } } // Turn "dict.Func" into a partial for "Func" bound to "dict". - if (selfdict != NULL - && (rettv->v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL)) { + if (selfdict != NULL && tv_is_func(*rettv)) { set_selfdict(rettv, selfdict); } - dict_unref(selfdict); + tv_dict_unref(selfdict); return ret; } -static void set_selfdict(typval_T *rettv, dict_T *selfdict) +void set_selfdict(typval_T *rettv, dict_T *selfdict) { // Don't do this when "dict.Func" is already a partial that was bound // explicitly (pt_auto is false). @@ -19707,7 +18446,7 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict) } else { pt->pt_argc = ret_pt->pt_argc; for (i = 0; i < pt->pt_argc; i++) { - copy_tv(&ret_pt->pt_argv[i], &pt->pt_argv[i]); + tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]); } } } @@ -19719,553 +18458,26 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict) } } -/* - * Free the memory for a variable type-value. - */ -void free_tv(typval_T *varp) -{ - if (varp != NULL) { - switch (varp->v_type) { - case VAR_FUNC: - func_unref(varp->vval.v_string); - // FALLTHROUGH - case VAR_STRING: - xfree(varp->vval.v_string); - break; - case VAR_PARTIAL: - partial_unref(varp->vval.v_partial); - break; - case VAR_LIST: - list_unref(varp->vval.v_list); - break; - case VAR_DICT: - dict_unref(varp->vval.v_dict); - break; - case VAR_SPECIAL: - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; - } - xfree(varp); - } -} - -#define TYPVAL_ENCODE_ALLOW_SPECIALS false - -#define TYPVAL_ENCODE_CONV_NIL(tv) \ - do { \ - tv->vval.v_special = kSpecialVarFalse; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ - TYPVAL_ENCODE_CONV_NIL(tv) - -#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ - do { \ - (void)num; \ - tv->vval.v_number = 0; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) - -#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ - do { \ - tv->vval.v_float = 0; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \ - do { \ - xfree(buf); \ - tv->vval.v_string = NULL; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) - -#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) - -static inline int _nothing_conv_func_start(typval_T *const tv, - char_u *const fun) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) -{ - tv->v_lock = VAR_UNLOCKED; - if (tv->v_type == VAR_PARTIAL) { - partial_T *const pt_ = tv->vval.v_partial; - if (pt_ != NULL && pt_->pt_refcount > 1) { - pt_->pt_refcount--; - tv->vval.v_partial = NULL; - return OK; - } - } else { - func_unref(fun); - if (fun != empty_string) { - xfree(fun); - } - tv->vval.v_string = NULL; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ - do { \ - if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \ - return OK; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) -#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) - -static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL -{ - if (tv->v_type == VAR_PARTIAL) { - partial_T *const pt = tv->vval.v_partial; - if (pt == NULL) { - return; - } - // Dictionary should already be freed by the time. - // If it was not freed then it is a part of the reference cycle. - assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID); - pt->pt_dict = NULL; - // As well as all arguments. - pt->pt_argc = 0; - assert(pt->pt_refcount <= 1); - partial_unref(pt); - tv->vval.v_partial = NULL; - assert(tv->v_lock == VAR_UNLOCKED); - } -} -#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID) - -#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ - do { \ - list_unref(tv->vval.v_list); \ - tv->vval.v_list = NULL; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - do { \ - assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ - dict_unref((dict_T *)dict); \ - *((dict_T **)&dict) = NULL; \ - if (tv != NULL) { \ - ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ - } \ - } while (0) - -static inline int _nothing_conv_real_list_after_start( - typval_T *const tv, MPConvStackVal *const mpsv) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT -{ - assert(tv != NULL); - tv->v_lock = VAR_UNLOCKED; - if (tv->vval.v_list->lv_refcount > 1) { - tv->vval.v_list->lv_refcount--; - tv->vval.v_list = NULL; - mpsv->data.l.li = NULL; - return OK; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) - -#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \ - do { \ - if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \ - goto typval_encode_stop_converting_one_item; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) - -static inline void _nothing_conv_list_end(typval_T *const tv) - FUNC_ATTR_ALWAYS_INLINE -{ - if (tv == NULL) { - return; - } - assert(tv->v_type == VAR_LIST); - list_T *const list = tv->vval.v_list; - list_unref(list); - tv->vval.v_list = NULL; -} -#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv) - -static inline int _nothing_conv_real_dict_after_start( - typval_T *const tv, dict_T **const dictp, const void *const nodictvar, - MPConvStackVal *const mpsv) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (tv != NULL) { - tv->v_lock = VAR_UNLOCKED; - } - if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) { - (*dictp)->dv_refcount--; - *dictp = NULL; - mpsv->data.d.todo = 0; - return OK; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) - -#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \ - do { \ - if (_nothing_conv_real_dict_after_start( \ - tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \ - &mpsv) != NOTDONE) { \ - goto typval_encode_stop_converting_one_item; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict) -#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) -#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) - -static inline void _nothing_conv_dict_end(typval_T *const tv, - dict_T **const dictp, - const void *const nodictvar) - FUNC_ATTR_ALWAYS_INLINE -{ - if ((const void *)dictp != nodictvar) { - dict_unref(*dictp); - *dictp = NULL; - } -} -#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ - _nothing_conv_dict_end(tv, (dict_T **)&dict, \ - (void *)&TYPVAL_ENCODE_NODICT_VAR) - -#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) - -#define TYPVAL_ENCODE_SCOPE static -#define TYPVAL_ENCODE_NAME nothing -#define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const -#define TYPVAL_ENCODE_FIRST_ARG_NAME ignored -#include "nvim/eval/typval_encode.c.h" -#undef TYPVAL_ENCODE_SCOPE -#undef TYPVAL_ENCODE_NAME -#undef TYPVAL_ENCODE_FIRST_ARG_TYPE -#undef TYPVAL_ENCODE_FIRST_ARG_NAME - -#undef TYPVAL_ENCODE_ALLOW_SPECIALS -#undef TYPVAL_ENCODE_CONV_NIL -#undef TYPVAL_ENCODE_CONV_BOOL -#undef TYPVAL_ENCODE_CONV_NUMBER -#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER -#undef TYPVAL_ENCODE_CONV_FLOAT -#undef TYPVAL_ENCODE_CONV_STRING -#undef TYPVAL_ENCODE_CONV_STR_STRING -#undef TYPVAL_ENCODE_CONV_EXT_STRING -#undef TYPVAL_ENCODE_CONV_FUNC_START -#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS -#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF -#undef TYPVAL_ENCODE_CONV_FUNC_END -#undef TYPVAL_ENCODE_CONV_EMPTY_LIST -#undef TYPVAL_ENCODE_CONV_EMPTY_DICT -#undef TYPVAL_ENCODE_CONV_LIST_START -#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START -#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS -#undef TYPVAL_ENCODE_CONV_LIST_END -#undef TYPVAL_ENCODE_CONV_DICT_START -#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START -#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK -#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY -#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS -#undef TYPVAL_ENCODE_CONV_DICT_END -#undef TYPVAL_ENCODE_CONV_RECURSE - -/// Free memory for a variable value and set the value to NULL or 0 -/// -/// @param[in,out] varp Value to free. -void clear_tv(typval_T *varp) -{ - if (varp != NULL && varp->v_type != VAR_UNKNOWN) { - const int evn_ret = encode_vim_to_nothing(varp, varp, "clear_tv argument"); - (void)evn_ret; - assert(evn_ret == OK); - } -} - -/* - * Set the value of a variable to NULL without freeing items. - */ -static void init_tv(typval_T *varp) -{ - if (varp != NULL) - memset(varp, 0, sizeof(typval_T)); -} - -/// Check that given value is a number or string -/// -/// Error messages are compatible with get_tv_number() previously used for the -/// same purpose in buf*() functions. Special values are not accepted (previous -/// behaviour: silently fail to find buffer). -/// -/// @param[in] tv Value to check. -/// -/// @return true if everything is OK, false otherwise. -bool tv_check_str_or_nr(const typval_T *const tv) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE -{ - switch (tv->v_type) { - case VAR_NUMBER: - case VAR_STRING: { - return true; - } - case VAR_FLOAT: { - EMSG(_("E805: Expected a Number or a String, Float found")); - return false; - } - case VAR_PARTIAL: - case VAR_FUNC: { - EMSG(_("E703: Expected a Number or a String, Funcref found")); - return false; - } - case VAR_LIST: { - EMSG(_("E745: Expected a Number or a String, List found")); - return false; - } - case VAR_DICT: { - EMSG(_("E728: Expected a Number or a String, Dictionary found")); - return false; - } - case VAR_SPECIAL: { - EMSG(_("E5300: Expected a Number or a String")); - return false; - } - case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); - return false; - } - } - assert(false); - return false; -} - -/* - * Get the number value of a variable. - * If it is a String variable, uses vim_str2nr(). - * For incompatible types, return 0. - * get_tv_number_chk() is similar to get_tv_number(), but informs the - * caller of incompatible types: it sets *denote to TRUE if "denote" - * is not NULL or returns -1 otherwise. - */ -long get_tv_number(typval_T *varp) -{ - int error = FALSE; - - return get_tv_number_chk(varp, &error); /* return 0L on error */ -} - -long get_tv_number_chk(typval_T *varp, int *denote) -{ - long n = 0L; - - switch (varp->v_type) { - case VAR_NUMBER: - return (long)(varp->vval.v_number); - case VAR_FLOAT: - EMSG(_("E805: Using a Float as a Number")); - break; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E703: Using a Funcref as a Number")); - break; - case VAR_STRING: - if (varp->vval.v_string != NULL) { - vim_str2nr(varp->vval.v_string, NULL, NULL, - STR2NR_ALL, &n, NULL, 0); - } - return n; - case VAR_LIST: - EMSG(_("E745: Using a List as a Number")); - break; - case VAR_DICT: - EMSG(_("E728: Using a Dictionary as a Number")); - break; - case VAR_SPECIAL: - switch (varp->vval.v_special) { - case kSpecialVarTrue: { - return 1; - } - case kSpecialVarFalse: - case kSpecialVarNull: { - return 0; - } - } - break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); - break; - } - if (denote == NULL) { - // useful for values that must be unsigned - n = -1; - } else { - *denote = true; - } - return n; -} - -static float_T get_tv_float(typval_T *varp) -{ - switch (varp->v_type) { - case VAR_NUMBER: - return (float_T)(varp->vval.v_number); - case VAR_FLOAT: - return varp->vval.v_float; - break; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E891: Using a Funcref as a Float")); - break; - case VAR_STRING: - EMSG(_("E892: Using a String as a Float")); - break; - case VAR_LIST: - EMSG(_("E893: Using a List as a Float")); - break; - case VAR_DICT: - EMSG(_("E894: Using a Dictionary as a Float")); - break; - default: - EMSG2(_(e_intern2), "get_tv_float()"); - break; - } - return 0; -} - -/* - * Get the lnum from the first argument. - * Also accepts ".", "$", etc., but that only works for the current buffer. - * Returns -1 on error. - */ -static linenr_T get_tv_lnum(typval_T *argvars) -{ - typval_T rettv; - linenr_T lnum; - - lnum = get_tv_number_chk(&argvars[0], NULL); - if (lnum == 0) { /* no valid number, try using line() */ - rettv.v_type = VAR_NUMBER; - f_line(argvars, &rettv, NULL); - lnum = rettv.vval.v_number; - clear_tv(&rettv); - } - return lnum; -} - -/* - * Get the lnum from the first argument. - * Also accepts "$", then "buf" is used. - * Returns 0 on error. - */ -static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) -{ - if (argvars[0].v_type == VAR_STRING - && argvars[0].vval.v_string != NULL - && argvars[0].vval.v_string[0] == '$' - && buf != NULL) - return buf->b_ml.ml_line_count; - return get_tv_number_chk(&argvars[0], NULL); -} - -/* - * Get the string value of a variable. - * If it is a Number variable, the number is converted into a string. - * get_tv_string() uses a single, static buffer. YOU CAN ONLY USE IT ONCE! - * get_tv_string_buf() uses a given buffer. - * If the String variable has never been set, return an empty string. - * Never returns NULL; - * get_tv_string_chk() and get_tv_string_buf_chk() are similar, but return - * NULL on error. - */ -static char_u *get_tv_string(const typval_T *varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET -{ - static char_u mybuf[NUMBUFLEN]; - - return get_tv_string_buf(varp, mybuf); -} - -static char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET -{ - char_u *res = get_tv_string_buf_chk(varp, buf); - - return res != NULL ? res : (char_u *)""; -} - -/// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! -char_u *get_tv_string_chk(const typval_T *varp) - FUNC_ATTR_NONNULL_ALL -{ - static char_u mybuf[NUMBUFLEN]; - - return get_tv_string_buf_chk(varp, mybuf); -} - -char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL -{ - switch (varp->v_type) { - case VAR_NUMBER: - sprintf((char *)buf, "%" PRId64, (int64_t)varp->vval.v_number); - return buf; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E729: using Funcref as a String")); - break; - case VAR_LIST: - EMSG(_("E730: using List as a String")); - break; - case VAR_DICT: - EMSG(_("E731: using Dictionary as a String")); - break; - case VAR_FLOAT: - EMSG(_(e_float_as_string)); - break; - case VAR_STRING: - if (varp->vval.v_string != NULL) - return varp->vval.v_string; - return (char_u *)""; - case VAR_SPECIAL: - STRCPY(buf, encode_special_var_names[varp->vval.v_special]); - return buf; - case VAR_UNKNOWN: - EMSG(_("E908: using an invalid value as a String")); - break; - } - return NULL; -} - -/* - * Find variable "name" in the list of variables. - * Return a pointer to it if found, NULL if not found. - * Careful: "a:0" variables don't have a name. - * When "htp" is not NULL we are writing to the variable, set "htp" to the - * hashtab_T used. - */ +// Find variable "name" in the list of variables. +// Return a pointer to it if found, NULL if not found. +// Careful: "a:0" variables don't have a name. +// When "htp" is not NULL we are writing to the variable, set "htp" to the +// hashtab_T used. static dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp, int no_autoload) { const char *varname; - hashtab_T *ht = find_var_ht(name, name_len, &varname); + hashtab_T *const ht = find_var_ht(name, name_len, &varname); if (htp != NULL) { *htp = ht; } if (ht == NULL) { return NULL; } - dictitem_T *ret = find_var_in_ht(ht, *name, - varname, name_len - (size_t)(varname - name), - no_autoload || htp != NULL); + dictitem_T *const ret = find_var_in_ht(ht, *name, + varname, + name_len - (size_t)(varname - name), + no_autoload || htp != NULL); if (ret != NULL) { return ret; } @@ -20329,7 +18541,7 @@ static dictitem_T *find_var_in_ht(hashtab_T *const ht, return NULL; } } - return HI2DI(hi); + return TV_DICT_HI2DI(hi); } // Get function call environment based on backtrace debug level @@ -20459,7 +18671,7 @@ static hashtab_T *find_var_ht(const char *name, const size_t name_len, /* * Get the string value of a (global/local) variable. - * Note: see get_tv_string() for how long the pointer remains valid. + * Note: see tv_get_string() for how long the pointer remains valid. * Returns NULL when it doesn't exist. */ char_u *get_var_value(const char *const name) @@ -20470,7 +18682,7 @@ char_u *get_var_value(const char *const name) if (v == NULL) { return NULL; } - return get_tv_string(&v->di_tv); + return (char_u *)tv_get_string(&v->di_tv); } /* @@ -20507,7 +18719,7 @@ void new_script_vars(scid_T id) * Initialize dictionary "dict" as a scope and set variable "dict_var" to * point to it. */ -void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope) +void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, int scope) { hash_init(&dict->dv_hashtab); dict->dv_lock = VAR_UNLOCKED; @@ -20530,7 +18742,7 @@ void unref_var_dict(dict_T *dict) /* Now the dict needs to be freed if no one else is using it, go back to * normal reference counting. */ dict->dv_refcount -= DO_NOT_FREE_CNT - 1; - dict_unref(dict); + tv_dict_unref(dict); } /* @@ -20561,9 +18773,9 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) // Free the variable. Don't remove it from the hashtab, // ht_array might change then. hash_clear() takes care of it // later. - v = HI2DI(hi); + v = TV_DICT_HI2DI(hi); if (free_val) { - clear_tv(&v->di_tv); + tv_clear(&v->di_tv); } if (v->di_flags & DI_FLAGS_ALLOC) { xfree(v); @@ -20580,10 +18792,10 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) */ static void delete_var(hashtab_T *ht, hashitem_T *hi) { - dictitem_T *di = HI2DI(hi); + dictitem_T *di = TV_DICT_HI2DI(hi); hash_remove(ht, hi); - clear_tv(&di->di_tv); + tv_clear(&di->di_tv); xfree(di); } @@ -20639,55 +18851,47 @@ static void list_one_var_a(const char *prefix, const char *name, } } -/* - * Set variable "name" to value in "tv". - * If the variable already exists, the value is updated. - * Otherwise the variable is created. - */ -static void -set_var ( - char_u *name, - typval_T *tv, - int copy /* make copy of value in "tv" */ -) +/// Set variable to the given value +/// +/// If the variable already exists, the value is updated. Otherwise the variable +/// is created. +/// +/// @param[in] name Variable name to set. +/// @param[in] name_len Length of the variable name. +/// @param tv Variable value. +/// @param[in] copy True if value in tv is to be copied. +static void set_var(const char *name, const size_t name_len, typval_T *const tv, + const bool copy) + FUNC_ATTR_NONNULL_ALL { dictitem_T *v; hashtab_T *ht; - typval_T oldtv; dict_T *dict; - const size_t name_len = STRLEN(name); - char_u *varname; - ht = find_var_ht_dict((const char *)name, name_len, (const char **)&varname, - &dict); - bool watched = is_watched(dict); - - if (watched) { - init_tv(&oldtv); - } + const char *varname; + ht = find_var_ht_dict(name, name_len, &varname, &dict); + const bool watched = tv_dict_is_watched(dict); if (ht == NULL || *varname == NUL) { EMSG2(_(e_illvar), name); return; } - v = find_var_in_ht(ht, 0, - (const char *)varname, name_len - (size_t)(varname - name), - true); + v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true); // Search in parent scope which is possible to reference from lambda if (v == NULL) { v = find_var_in_scoped_ht((const char *)name, name_len, true); } - if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) - && var_check_func_name(name, v == NULL)) { + if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) { return; } + typval_T oldtv = TV_INITIAL_VALUE; if (v != NULL) { // existing variable, need to clear the value - if (var_check_ro(v->di_flags, (const char *)name, name_len) - || tv_check_lock(v->di_tv.v_lock, (const char *)name, name_len)) { + if (var_check_ro(v->di_flags, name, name_len) + || tv_check_lock(v->di_tv.v_lock, name, name_len)) { return; } @@ -20696,19 +18900,19 @@ set_var ( if (ht == &vimvarht) { if (v->di_tv.v_type == VAR_STRING) { xfree(v->di_tv.vval.v_string); - if (copy || tv->v_type != VAR_STRING) - v->di_tv.vval.v_string = vim_strsave(get_tv_string(tv)); - else { + if (copy || tv->v_type != VAR_STRING) { + v->di_tv.vval.v_string = (char_u *)xstrdup(tv_get_string(tv)); + } else { // Take over the string to avoid an extra alloc/free. v->di_tv.vval.v_string = tv->vval.v_string; tv->vval.v_string = NULL; } return; } else if (v->di_tv.v_type == VAR_NUMBER) { - v->di_tv.vval.v_number = get_tv_number(tv); - if (STRCMP(varname, "searchforward") == 0) + v->di_tv.vval.v_number = tv_get_number(tv); + if (strcmp(varname, "searchforward") == 0) { set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); - else if (STRCMP(varname, "hlsearch") == 0) { + } else if (strcmp(varname, "hlsearch") == 0) { no_hlsearch = !v->di_tv.vval.v_number; redraw_all_later(SOME_VALID); } @@ -20719,23 +18923,24 @@ set_var ( } if (watched) { - copy_tv(&v->di_tv, &oldtv); + tv_copy(&v->di_tv, &oldtv); } - clear_tv(&v->di_tv); - } else { /* add a new variable */ - /* Can't add "v:" variable. */ + tv_clear(&v->di_tv); + } else { // Add a new variable. + // Can't add "v:" variable. if (ht == &vimvarht) { - EMSG2(_(e_illvar), name); + emsgf(_(e_illvar), name); return; } - /* Make sure the variable name is valid. */ - if (!valid_varname(varname)) + // Make sure the variable name is valid. + if (!valid_varname(varname)) { return; + } - v = xmalloc(sizeof(dictitem_T) + STRLEN(varname)); + v = xmalloc(sizeof(dictitem_T) + strlen(varname)); STRCPY(v->di_key, varname); - if (hash_add(ht, DI2HIKEY(v)) == FAIL) { + if (tv_dict_add(dict, v) == FAIL) { xfree(v); return; } @@ -20743,19 +18948,19 @@ set_var ( } if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { - copy_tv(tv, &v->di_tv); + tv_copy(tv, &v->di_tv); } else { v->di_tv = *tv; v->di_tv.v_lock = 0; - init_tv(tv); + tv_init(tv); } if (watched) { if (oldtv.v_type == VAR_UNKNOWN) { - dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); } else { - dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); - clear_tv(&oldtv); + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); + tv_clear(&oldtv); } } } @@ -20770,8 +18975,8 @@ set_var ( /// /// @return True if variable is read-only: either always or in sandbox when /// sandbox is enabled, false otherwise. -static bool var_check_ro(const int flags, const char *const name, - const size_t name_len) +bool var_check_ro(const int flags, const char *const name, + const size_t name_len) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (flags & DI_FLAGS_RO) { @@ -20806,22 +19011,24 @@ static bool var_check_fixed(const int flags, const char *const name, return false; } -/* - * Check if a funcref is assigned to a valid variable name. - * Return TRUE and give an error if not. - */ -static int -var_check_func_name ( - char_u *name, /* points to start of variable name */ - int new_var /* TRUE when creating the variable */ -) +// TODO(ZyX-I): move to eval/expressions + +/// Check if name is a valid name to assign funcref to +/// +/// @param[in] name Possible function/funcref name. +/// @param[in] new_var True if it is a name for a variable. +/// +/// @return false in case of error, true in case of success. Also gives an +/// error message if appropriate. +bool var_check_func_name(const char *const name, const bool new_var) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { // Allow for w: b: s: and t:. if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':') && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] : name[0])) { EMSG2(_("E704: Funcref variable name must start with a capital: %s"), name); - return TRUE; + return false; } // Don't allow hiding a function. When "v" is not NULL we might be // assigning another function to the same var, the type is checked @@ -20829,116 +19036,33 @@ var_check_func_name ( if (new_var && function_exists((const char *)name, false)) { EMSG2(_("E705: Variable name conflicts with existing function: %s"), name); - return true; + return false; } - return false; + return true; } -/* - * Check if a variable name is valid. - * Return FALSE and give an error if not. - */ -static int valid_varname(char_u *varname) -{ - char_u *p; - - for (p = varname; *p != NUL; ++p) - if (!eval_isnamec1(*p) && (p == varname || !ascii_isdigit(*p)) - && *p != AUTOLOAD_CHAR) { - EMSG2(_(e_illvar), varname); - return FALSE; - } - return TRUE; -} +// TODO(ZyX-I): move to eval/expressions -/// Check whether typval is locked +/// Check if a variable name is valid /// -/// Also gives an error message. +/// @param[in] varname Variable name to check. /// -/// @param[in] lock Lock status. -/// @param[in] name Value name, for use in error message. -/// @param[in] name_len Value name length. -/// -/// @return True if value is locked. -static bool tv_check_lock(const VarLockStatus lock, - const char *const name, - const size_t name_len) - FUNC_ATTR_WARN_UNUSED_RESULT +/// @return false when variable name is not valid, true when it is. Also gives +/// an error message if appropriate. +bool valid_varname(const char *varname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - const char *error_message = NULL; - switch (lock) { - case VAR_UNLOCKED: { + for (const char *p = varname; *p != NUL; p++) { + if (!eval_isnamec1((int)(uint8_t)(*p)) + && (p == varname || !ascii_isdigit(*p)) + && *p != AUTOLOAD_CHAR) { + emsgf(_(e_illvar), varname); return false; } - case VAR_LOCKED: { - error_message = N_("E741: Value is locked: %.*s"); - break; - } - case VAR_FIXED: { - error_message = N_("E742: Cannot change value of %.*s"); - break; - } } - assert(error_message != NULL); - - const char *const unknown_name = _("Unknown"); - - emsgf(_(error_message), (name != NULL ? name_len : strlen(unknown_name)), - (name != NULL ? name : unknown_name)); - return true; } -/* - * Copy the values from typval_T "from" to typval_T "to". - * When needed allocates string or increases reference count. - * Does not make a copy of a list or dict but copies the reference! - * It is OK for "from" and "to" to point to the same item. This is used to - * make a copy later. - */ -void copy_tv(typval_T *from, typval_T *to) -{ - to->v_type = from->v_type; - to->v_lock = 0; - memmove(&to->vval, &from->vval, sizeof(to->vval)); - switch (from->v_type) { - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_SPECIAL: - break; - case VAR_STRING: - case VAR_FUNC: - if (from->vval.v_string != NULL) { - to->vval.v_string = vim_strsave(from->vval.v_string); - if (from->v_type == VAR_FUNC) { - func_ref(to->vval.v_string); - } - } - break; - case VAR_PARTIAL: - if (from->vval.v_partial == NULL) { - to->vval.v_partial = NULL; - } else { - to->vval.v_partial = from->vval.v_partial; - (to->vval.v_partial->pt_refcount)++; - } - break; - case VAR_LIST: - if (from->vval.v_list != NULL) { - to->vval.v_list->lv_refcount++; - } - break; - case VAR_DICT: - if (from->vval.v_dict != NULL) { - to->vval.v_dict->dv_refcount++; - } - break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "copy_tv(UNKNOWN)"); - break; - } -} - /// Make a copy of an item /// /// Lists and Dictionaries are also copied. @@ -20954,7 +19078,7 @@ void copy_tv(typval_T *from, typval_T *to) /// list[1]`) var_item_copy with zero copyID will emit /// a copy with (`copy[0] isnot copy[1]`), with non-zero it /// will emit a copy with (`copy[0] is copy[1]`) like in the -/// original list. Not use when deep is false. +/// original list. Not used when deep is false. int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *const to, @@ -20977,11 +19101,12 @@ int var_item_copy(const vimconv_T *const conv, case VAR_FUNC: case VAR_PARTIAL: case VAR_SPECIAL: - copy_tv(from, to); + tv_copy(from, to); break; case VAR_STRING: - if (conv == NULL || conv->vc_type == CONV_NONE) { - copy_tv(from, to); + if (conv == NULL || conv->vc_type == CONV_NONE + || from->vval.v_string == NULL) { + tv_copy(from, to); } else { to->v_type = VAR_STRING; to->v_lock = 0; @@ -21003,10 +19128,11 @@ int var_item_copy(const vimconv_T *const conv, to->vval.v_list = from->vval.v_list->lv_copylist; ++to->vval.v_list->lv_refcount; } else { - to->vval.v_list = list_copy(conv, from->vval.v_list, deep, copyID); + to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); } - if (to->vval.v_list == NULL) + if (to->vval.v_list == NULL && from->vval.v_list != NULL) { ret = FAIL; + } break; case VAR_DICT: to->v_type = VAR_DICT; @@ -21018,10 +19144,11 @@ int var_item_copy(const vimconv_T *const conv, to->vval.v_dict = from->vval.v_dict->dv_copydict; ++to->vval.v_dict->dv_refcount; } else { - to->vval.v_dict = dict_copy(conv, from->vval.v_dict, deep, copyID); + to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID); } - if (to->vval.v_dict == NULL) + if (to->vval.v_dict == NULL && from->vval.v_dict != NULL) { ret = FAIL; + } break; case VAR_UNKNOWN: EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)"); @@ -21105,7 +19232,7 @@ void ex_echo(exarg_T *eap) } xfree(tofree); } - clear_tv(&rettv); + tv_clear(&rettv); arg = skipwhite(arg); } eap->nextcmd = check_nextcmd(arg); @@ -21149,7 +19276,6 @@ void ex_execute(exarg_T *eap) int ret = OK; char_u *p; garray_T ga; - int len; int save_did_emsg; ga_init(&ga, 1, 80); @@ -21171,16 +19297,17 @@ void ex_execute(exarg_T *eap) } if (!eap->skip) { - p = get_tv_string(&rettv); - len = (int)STRLEN(p); + const char *const argstr = tv_get_string(&rettv); + const size_t len = strlen(argstr); ga_grow(&ga, len + 2); - if (!GA_EMPTY(&ga)) + if (!GA_EMPTY(&ga)) { ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; - STRCPY((char_u *)(ga.ga_data) + ga.ga_len, p); + } + memcpy((char_u *)(ga.ga_data) + ga.ga_len, argstr, len + 1); ga.ga_len += len; } - clear_tv(&rettv); + tv_clear(&rettv); arg = skipwhite(arg); } @@ -21424,9 +19551,7 @@ void ex_function(exarg_T *eap) arg = name; else arg = fudi.fd_newkey; - if (arg != NULL && (fudi.fd_di == NULL - || (fudi.fd_di->di_tv.v_type != VAR_FUNC - && fudi.fd_di->di_tv.v_type != VAR_PARTIAL))) { + if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) { int j = (*arg == K_SPECIAL) ? 3 : 0; while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) : eval_isnamec(arg[j]))) @@ -21732,16 +19857,17 @@ void ex_function(exarg_T *eap) if (fudi.fd_dict != NULL) { if (fudi.fd_di == NULL) { - /* add new dict entry */ - fudi.fd_di = dictitem_alloc(fudi.fd_newkey); - if (dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) { + // Add new dict entry + fudi.fd_di = tv_dict_item_alloc((const char *)fudi.fd_newkey); + if (tv_dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) { xfree(fudi.fd_di); xfree(fp); goto erret; } - } else - /* overwrite existing dict entry */ - clear_tv(&fudi.fd_di->di_tv); + } else { + // Overwrite existing dict entry. + tv_clear(&fudi.fd_di->di_tv); + } fudi.fd_di->di_tv.v_type = VAR_FUNC; fudi.fd_di->di_tv.v_lock = 0; fudi.fd_di->di_tv.vval.v_string = vim_strsave(name); @@ -21802,20 +19928,19 @@ ret_free: /// Advances "pp" to just after the function name (if no error). /// /// @return the function name in allocated memory, or NULL for failure. -char_u * +static char_u * trans_function_name( char_u **pp, - int skip, /* only find the end, don't evaluate */ + int skip, // only find the end, don't evaluate int flags, funcdict_T *fdp, // return: info about dictionary used partial_T **partial // return: partial of a FuncRef ) { char_u *name = NULL; - char_u *start; - char_u *end; + const char_u *start; + const char_u *end; int lead; - char_u sid_buf[20]; int len; lval_T lv; @@ -21839,9 +19964,9 @@ trans_function_name( start += lead; } - /* Note that TFN_ flags use the same values as GLV_ flags. */ - end = get_lval(start, NULL, &lv, FALSE, skip, flags, - lead > 2 ? 0 : FNE_CHECK_START); + // Note that TFN_ flags use the same values as GLV_ flags. + end = get_lval((char_u *)start, NULL, &lv, false, skip, flags, + lead > 2 ? 0 : FNE_CHECK_START); if (end == start) { if (!skip) EMSG(_("E129: Function name required")); @@ -21854,10 +19979,12 @@ trans_function_name( * interrupt, or an exception. */ if (!aborting()) { - if (end != NULL) - EMSG2(_(e_invarg2), start); - } else - *pp = find_name_end(start, NULL, NULL, FNE_INCL_BR); + if (end != NULL) { + emsgf(_(e_invarg2), start); + } + } else { + *pp = (char_u *)find_name_end(start, NULL, NULL, FNE_INCL_BR); + } goto theend; } @@ -21870,11 +19997,11 @@ trans_function_name( } if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) { name = vim_strsave(lv.ll_tv->vval.v_string); - *pp = end; + *pp = (char_u *)end; } else if (lv.ll_tv->v_type == VAR_PARTIAL && lv.ll_tv->vval.v_partial != NULL) { name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial)); - *pp = end; + *pp = (char_u *)end; if (partial != NULL) { *partial = lv.ll_tv->vval.v_partial; } @@ -21884,7 +20011,7 @@ trans_function_name( || fdp->fd_newkey == NULL)) { EMSG(_(e_funcref)); } else { - *pp = end; + *pp = (char_u *)end; } name = NULL; } @@ -21892,17 +20019,17 @@ trans_function_name( } if (lv.ll_name == NULL) { - /* Error found, but continue after the function name. */ - *pp = end; + // Error found, but continue after the function name. + *pp = (char_u *)end; goto theend; } /* Check if the name is a Funcref. If so, use the value. */ if (lv.ll_exp_name != NULL) { - len = (int)STRLEN(lv.ll_exp_name); - name = deref_func_name((const char *)lv.ll_exp_name, &len, partial, + len = (int)strlen(lv.ll_exp_name); + name = deref_func_name(lv.ll_exp_name, &len, partial, flags & TFN_NO_AUTOLOAD); - if (name == lv.ll_exp_name) { + if ((const char *)name == lv.ll_exp_name) { name = NULL; } } else if (!(flags & TFN_NO_DEREF)) { @@ -21915,7 +20042,7 @@ trans_function_name( } if (name != NULL) { name = vim_strsave(name); - *pp = end; + *pp = (char_u *)end; if (strncmp((char *)name, "<SNR>", 5) == 0) { // Change "<SNR>" to the byte sequence. name[0] = K_SPECIAL; @@ -21927,12 +20054,13 @@ trans_function_name( } if (lv.ll_exp_name != NULL) { - len = (int)STRLEN(lv.ll_exp_name); + len = (int)strlen(lv.ll_exp_name); if (lead <= 2 && lv.ll_name == lv.ll_exp_name - && STRNCMP(lv.ll_name, "s:", 2) == 0) { - /* When there was "s:" already or the name expanded to get a - * leading "s:" then remove it. */ + && lv.ll_name_len >= 2 && memcmp(lv.ll_name, "s:", 2) == 0) { + // When there was "s:" already or the name expanded to get a + // leading "s:" then remove it. lv.ll_name += 2; + lv.ll_name_len -= 2; len -= 2; lead = 2; } @@ -21940,38 +20068,41 @@ trans_function_name( // Skip over "s:" and "g:". if (lead == 2 || (lv.ll_name[0] == 'g' && lv.ll_name[1] == ':')) { lv.ll_name += 2; + lv.ll_name_len -= 2; } - len = (int)(end - lv.ll_name); + len = (int)((const char *)end - lv.ll_name); } - /* - * Copy the function name to allocated memory. - * Accept <SID>name() inside a script, translate into <SNR>123_name(). - * Accept <SNR>123_name() outside a script. - */ - if (skip) - lead = 0; /* do nothing */ - else if (lead > 0) { + size_t sid_buf_len = 0; + char sid_buf[20]; + + // Copy the function name to allocated memory. + // Accept <SID>name() inside a script, translate into <SNR>123_name(). + // Accept <SNR>123_name() outside a script. + if (skip) { + lead = 0; // do nothing + } else if (lead > 0) { lead = 3; - if ((lv.ll_exp_name != NULL && eval_fname_sid((const char *)lv.ll_exp_name)) + if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) || eval_fname_sid((const char *)(*pp))) { // It's "s:" or "<SID>". if (current_SID <= 0) { EMSG(_(e_usingsid)); goto theend; } - sprintf((char *)sid_buf, "%" PRId64 "_", (int64_t)current_SID); - lead += (int)STRLEN(sid_buf); + sid_buf_len = snprintf(sid_buf, sizeof(sid_buf), + "%" PRIdSCID "_", current_SID); + lead += sid_buf_len; } } else if (!(flags & TFN_INT) - && builtin_function((const char *)lv.ll_name, len)) { + && builtin_function(lv.ll_name, lv.ll_name_len)) { EMSG2(_("E128: Function name must start with a capital or \"s:\": %s"), start); goto theend; } if (!skip && !(flags & TFN_QUIET) && !(flags & TFN_NO_DEREF)) { - char_u *cp = vim_strchr(lv.ll_name, ':'); + char_u *cp = xmemrchr(lv.ll_name, ':', lv.ll_name_len); if (cp != NULL && cp < end) { EMSG2(_("E884: Function name cannot contain a colon: %s"), start); @@ -21984,12 +20115,13 @@ trans_function_name( name[0] = K_SPECIAL; name[1] = KS_EXTRA; name[2] = (int)KE_SNR; - if (lead > 3) /* If it's "<SID>" */ - STRCPY(name + 3, sid_buf); + if (sid_buf_len > 0) { // If it's "<SID>" + memcpy(name + 3, sid_buf, sid_buf_len); + } } - memmove(name + lead, lv.ll_name, (size_t)len); + memmove(name + lead, lv.ll_name, len); name[lead + len] = NUL; - *pp = end; + *pp = (char_u *)end; theend: clear_lval(&lv); @@ -22003,7 +20135,7 @@ theend: */ static int eval_fname_script(const char *const p) { - // Use mb_stricmp() because in Turkish comparing the "I" may not work with + // Use mb_strnicmp() because in Turkish comparing the "I" may not work with // the standard library function. if (p[0] == '<' && (mb_strnicmp((char_u *)p + 1, (char_u *)"SID>", 4) == 0 @@ -22018,8 +20150,8 @@ static int eval_fname_script(const char *const p) /// Check whether function name starts with <SID> or s: /// -/// Only works for names previously checked by eval_fname_script(), if it -/// returned non-zero. +/// @warning Only works for names previously checked by eval_fname_script(), if +/// it returned non-zero. /// /// @param[in] name Name to check. /// @@ -22168,14 +20300,15 @@ bool translated_function_exists(const char *name) /// @return True if it exists, false otherwise. static bool function_exists(const char *const name, bool no_deref) { - char_u *nm = (char_u *)name; + const char_u *nm = (const char_u *)name; bool n = false; int flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD; if (no_deref) { flag |= TFN_NO_DEREF; } - char *const p = (char *)trans_function_name(&nm, false, flag, NULL, NULL); + char *const p = (char *)trans_function_name((char_u **)&nm, false, flag, NULL, + NULL); nm = skipwhite(nm); /* Only accept "funcname", "funcname ", "funcname (..." and @@ -22559,9 +20692,9 @@ void ex_delfunction(exarg_T *eap) } if (fudi.fd_dict != NULL) { - /* Delete the dict item that refers to the function, it will - * invoke func_unref() and possibly delete the function. */ - dictitem_remove(fudi.fd_dict, fudi.fd_di); + // Delete the dict item that refers to the function, it will + // invoke func_unref() and possibly delete the function. + tv_dict_item_remove(fudi.fd_dict, fudi.fd_di); } else { // A normal function (not a numbered function or lambda) has a // refcount of 1 for the entry in the hashtable. When deleting @@ -22665,17 +20798,16 @@ void func_unref(char_u *name) abort(); #endif } - if (fp != NULL && --fp->uf_refcount <= 0) { - // Only delete it when it's not being used. Otherwise it's done - // when "uf_calls" becomes zero. - if (fp->uf_calls == 0) { - func_clear_free(fp, false); - } - } + func_ptr_unref(fp); } /// Unreference a Function: decrement the reference count and free it when it /// becomes zero. +/// Unreference user function, freeing it if needed +/// +/// Decrements the reference count and frees when it becomes zero. +/// +/// @param fp Function to unreference. void func_ptr_unref(ufunc_T *fp) { if (fp != NULL && --fp->uf_refcount <= 0) { @@ -22713,17 +20845,19 @@ void func_ptr_ref(ufunc_T *fp) } } -/// Call a user function. -static void -call_user_func( - ufunc_T *fp, // pointer to function - int argcount, // nr of args - typval_T *argvars, // arguments - typval_T *rettv, // return value - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - dict_T *selfdict // Dictionary for "self" -) +/// Call a user function +/// +/// @param fp Function to call. +/// @param[in] argcount Number of arguments. +/// @param argvars Arguments. +/// @param[out] rettv Return value. +/// @param[in] firstline First line of range. +/// @param[in] lastline Last line of range. +/// @param selfdict Dictionary for "self" for dictionary functions. +void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, + typval_T *rettv, linenr_T firstline, linenr_T lastline, + dict_T *selfdict) + FUNC_ATTR_NONNULL_ARG(1, 3, 4) { char_u *save_sourcing_name; linenr_T save_sourcing_lnum; @@ -22789,15 +20923,15 @@ call_user_func( // Init l: variables. init_var_dict(&fc->l_vars, &fc->l_vars_var, VAR_DEF_SCOPE); if (selfdict != NULL) { - /* Set l:self to "selfdict". Use "name" to avoid a warning from - * some compiler that checks the destination size. */ - v = &fc->fixvar[fixvar_idx++].var; + // Set l:self to "selfdict". Use "name" to avoid a warning from + // some compiler that checks the destination size. + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; #ifndef __clang_analyzer__ name = v->di_key; STRCPY(name, "self"); #endif v->di_flags = DI_FLAGS_RO + DI_FLAGS_FIX; - hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_vars, v); v->di_tv.v_type = VAR_DICT; v->di_tv.v_lock = 0; v->di_tv.vval.v_dict = selfdict; @@ -22810,17 +20944,17 @@ call_user_func( * Set a:000 to a list with room for the "..." arguments. */ init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0", - (varnumber_T)(argcount - fp->uf_args.ga_len)); - /* Use "name" to avoid a warning from some compiler that checks the - * destination size. */ - v = &fc->fixvar[fixvar_idx++].var; + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0", + (varnumber_T)(argcount - fp->uf_args.ga_len)); + // Use "name" to avoid a warning from some compiler that checks the + // destination size. + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; #ifndef __clang_analyzer__ name = v->di_key; STRCPY(name, "000"); #endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_avars, v); v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_list = &fc->l_varlist; @@ -22831,10 +20965,10 @@ call_user_func( // Set a:firstline to "firstline" and a:lastline to "lastline". // Set a:name to named arguments. // Set a:N to the "..." arguments. - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline", - (varnumber_T)firstline); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline", - (varnumber_T)lastline); + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + "firstline", (varnumber_T)firstline); + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + "lastline", (varnumber_T)lastline); for (int i = 0; i < argcount; i++) { bool addlocal = false; @@ -22851,7 +20985,7 @@ call_user_func( name = numbuf; } if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) { - v = &fc->fixvar[fixvar_idx++].var; + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; } else { v = xmalloc(sizeof(dictitem_T) + STRLEN(name)); @@ -22867,14 +21001,14 @@ call_user_func( if (addlocal) { // Named arguments can be accessed without the "a:" prefix in lambda // expressions. Add to the l: dict. - copy_tv(&v->di_tv, &v->di_tv); - hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v)); + tv_copy(&v->di_tv, &v->di_tv); + tv_dict_add(&fc->l_vars, v); } else { - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_avars, v); } if (ai >= 0 && ai < MAX_FUNC_ARGS) { - list_append(&fc->l_varlist, &fc->l_listitems[ai]); + tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]); fc->l_listitems[ai].li_tv = argvars[i]; fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; } @@ -22982,7 +21116,7 @@ call_user_func( // when the function was aborted because of an error, return -1 if ((did_emsg && (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) { - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = -1; } @@ -23066,29 +21200,22 @@ call_user_func( && fc->fc_refcount <= 0) { free_funccal(fc, false); } else { - hashitem_T *hi; - listitem_T *li; - int todo; - // "fc" is still in use. This can happen when returning "a:000", // assigning "l:" to a global variable or defining a closure. // Link "fc" in the list for garbage collection later. fc->caller = previous_funccal; previous_funccal = fc; - /* Make a copy of the a: variables, since we didn't do that above. */ - todo = (int)fc->l_avars.dv_hashtab.ht_used; - for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - v = HI2DI(hi); - copy_tv(&v->di_tv, &v->di_tv); - } - } + // Make a copy of the a: variables, since we didn't do that above. + TV_DICT_ITER(&fc->l_avars, di, { + tv_copy(&di->di_tv, &di->di_tv); + }); - /* Make a copy of the a:000 items, since we didn't do that above. */ - for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) - copy_tv(&li->li_tv, &li->li_tv); + // Make a copy of the a:000 items, since we didn't do that above. + for (listitem_T *li = fc->l_varlist.lv_first; li != NULL; + li = li->li_next) { + tv_copy(&li->li_tv, &li->li_tv); + } } if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) { @@ -23178,7 +21305,7 @@ free_funccal ( // Free the a:000 variables if they were allocated. if (free_val) { for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) { - clear_tv(&li->li_tv); + tv_clear(&li->li_tv); } } @@ -23195,7 +21322,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr) STRCPY(v->di_key, name); #endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&dp->dv_hashtab, DI2HIKEY(v)); + tv_dict_add(dp, v); v->di_tv.v_type = VAR_NUMBER; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_number = nr; @@ -23221,10 +21348,11 @@ void ex_return(exarg_T *eap) eap->nextcmd = NULL; if ((*arg != NUL && *arg != '|' && *arg != '\n') && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) { - if (!eap->skip) - returning = do_return(eap, FALSE, TRUE, &rettv); - else - clear_tv(&rettv); + if (!eap->skip) { + returning = do_return(eap, false, true, &rettv); + } else { + tv_clear(&rettv); + } } /* It's safer to return also on error. */ else if (!eap->skip) { @@ -23311,7 +21439,7 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) * a return immediately after reanimation, the value is already * there. */ if (!reanimate && rettv != NULL) { - clear_tv(current_funccal->rettv); + tv_clear(current_funccal->rettv); *current_funccal->rettv = *(typval_T *)rettv; if (!is_cmd) xfree(rettv); @@ -23322,14 +21450,6 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) } /* - * Free the variable with a pending return value. - */ -void discard_pending_return(void *rettv) -{ - free_tv((typval_T *)rettv); -} - -/* * Generate a return command for producing the value of "rettv". The result * is an allocated string. Used by report_pending() for verbose messages. */ @@ -23590,7 +21710,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, hi = globvarht.ht_array; while ((size_t) (hi - hifirst) < hinum && (HASHITEM_EMPTY(hi) - || var_flavour(HI2DI(hi)->di_key) != VAR_FLAVOUR_SHADA)) { + || var_flavour(hi->hi_key) != VAR_FLAVOUR_SHADA)) { hi++; } if ((size_t) (hi - hifirst) == hinum) { @@ -23599,11 +21719,10 @@ const void *var_shada_iter(const void *const iter, const char **const name, } else { hi = (const hashitem_T *) iter; } - *name = (char *) HI2DI(hi)->di_key; - copy_tv(&(HI2DI(hi)->di_tv), rettv); - while ((size_t) (++hi - hifirst) < hinum) { - if (!HASHITEM_EMPTY(hi) - && var_flavour(HI2DI(hi)->di_key) == VAR_FLAVOUR_SHADA) { + *name = (char *)TV_DICT_HI2DI(hi)->di_key; + tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv); + while ((size_t)(++hi - hifirst) < hinum) { + if (!HASHITEM_EMPTY(hi) && var_flavour(hi->hi_key) == VAR_FLAVOUR_SHADA) { return hi; } } @@ -23614,62 +21733,55 @@ void var_set_global(const char *const name, typval_T vartv) { funccall_T *const saved_current_funccal = current_funccal; current_funccal = NULL; - set_var((char_u *) name, &vartv, false); + set_var(name, strlen(name), &vartv, false); current_funccal = saved_current_funccal; } int store_session_globals(FILE *fd) { - hashitem_T *hi; - dictitem_T *this_var; - int todo; - char_u *p, *t; - - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - this_var = HI2DI(hi); - if ((this_var->di_tv.v_type == VAR_NUMBER - || this_var->di_tv.v_type == VAR_STRING) - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - /* Escape special characters with a backslash. Turn a LF and - * CR into \n and \r. */ - p = vim_strsave_escaped(get_tv_string(&this_var->di_tv), - (char_u *)"\\\"\n\r"); - for (t = p; *t != NUL; ++t) - if (*t == '\n') - *t = 'n'; - else if (*t == '\r') - *t = 'r'; - if ((fprintf(fd, "let %s = %c%s%c", - this_var->di_key, - (this_var->di_tv.v_type == VAR_STRING) ? '"' - : ' ', - p, - (this_var->di_tv.v_type == VAR_STRING) ? '"' - : ' ') < 0) - || put_eol(fd) == FAIL) { - xfree(p); - return FAIL; + TV_DICT_ITER(&globvardict, this_var, { + if ((this_var->di_tv.v_type == VAR_NUMBER + || this_var->di_tv.v_type == VAR_STRING) + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { + // Escape special characters with a backslash. Turn a LF and + // CR into \n and \r. + char_u *const p = vim_strsave_escaped( + (const char_u *)tv_get_string(&this_var->di_tv), + (const char_u *)"\\\"\n\r"); + for (char_u *t = p; *t != NUL; t++) { + if (*t == '\n') { + *t = 'n'; + } else if (*t == '\r') { + *t = 'r'; } + } + if ((fprintf(fd, "let %s = %c%s%c", + this_var->di_key, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' '), + p, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' ')) < 0) + || put_eol(fd) == FAIL) { xfree(p); - } else if (this_var->di_tv.v_type == VAR_FLOAT - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - float_T f = this_var->di_tv.vval.v_float; - int sign = ' '; - - if (f < 0) { - f = -f; - sign = '-'; - } - if ((fprintf(fd, "let %s = %c%f", - this_var->di_key, sign, f) < 0) - || put_eol(fd) == FAIL) - return FAIL; + return FAIL; + } + xfree(p); + } else if (this_var->di_tv.v_type == VAR_FLOAT + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { + float_T f = this_var->di_tv.vval.v_float; + int sign = ' '; + + if (f < 0) { + f = -f; + sign = '-'; + } + if ((fprintf(fd, "let %s = %c%f", this_var->di_key, sign, f) < 0) + || put_eol(fd) == FAIL) { + return FAIL; } } - } + }); return OK; } @@ -23706,7 +21818,8 @@ void ex_oldfiles(exarg_T *eap) for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) { msg_outnum(++nr); MSG_PUTS(": "); - msg_outtrans(get_tv_string(&li->li_tv)); + msg_outtrans((char_u *)tv_get_string(&li->li_tv)); + msg_clr_eos(); msg_putchar('\n'); ui_flush(); /* output one line at a time */ os_breakcheck(); @@ -23720,15 +21833,15 @@ void ex_oldfiles(exarg_T *eap) nr = prompt_for_number(false); msg_starthere(); if (nr > 0 && nr <= l->lv_len) { - char_u *p = list_find_str(l, nr); + const char *const p = tv_list_find_str(l, nr - 1); if (p == NULL) { return; } - p = expand_env_save(p); - eap->arg = p; + char *const s = (char *)expand_env_save((char_u *)p); + eap->arg = (char_u *)s; eap->cmdidx = CMD_edit; do_exedit(eap, NULL); - xfree(p); + xfree(s); } } } @@ -24093,7 +22206,7 @@ static inline TerminalJobData *common_job_init(char **argv, bool pty, bool rpc, bool detach, - char *cwd) + const char *cwd) { TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData)); data->stopped = false; @@ -24127,9 +22240,9 @@ static inline TerminalJobData *common_job_init(char **argv, static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout, Callback *on_stderr, Callback *on_exit) { - if (get_dict_callback(vopts, "on_stdout", on_stdout) - && get_dict_callback(vopts, "on_stderr", on_stderr) - && get_dict_callback(vopts, "on_exit", on_exit)) { + if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), on_stdout) + &&tv_dict_get_callback(vopts, S_LEN("on_stderr"), on_stderr) + && tv_dict_get_callback(vopts, S_LEN("on_exit"), on_exit)) { vopts->dv_refcount++; return true; } @@ -24216,7 +22329,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, JobEvent event_data; event_data.received = NULL; if (buf) { - event_data.received = list_alloc(); + event_data.received = tv_list_alloc(); char *ptr = buf; size_t remaining = count; size_t off = 0; @@ -24224,7 +22337,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, while (off < remaining) { // append the line if (ptr[off] == NL) { - list_append_string(event_data.received, (uint8_t *)ptr, off); + tv_list_append_string(event_data.received, ptr, off); size_t skip = off + 1; ptr += skip; remaining -= skip; @@ -24237,7 +22350,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, } off++; } - list_append_string(event_data.received, (uint8_t *)ptr, off); + tv_list_append_string(event_data.received, ptr, off); } else { event_data.status = status; } @@ -24383,10 +22496,9 @@ static void on_job_event(JobEvent *ev) argv[2].v_lock = 0; argv[2].vval.v_string = (uint8_t *)ev->type; - typval_T rettv; - init_tv(&rettv); + typval_T rettv = TV_INITIAL_VALUE; callback_call(ev->callback, 3, argv, &rettv); - clear_tv(&rettv); + tv_clear(&rettv); } static TerminalJobData *find_job(uint64_t id) @@ -24409,8 +22521,8 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) return; } - list_T *args = list_alloc(); - list_append_string(args, argvars[0].vval.v_string, -1); + list_T *args = tv_list_alloc(); + tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1); *rettv = eval_call_provider(name, "eval", args); } @@ -24442,7 +22554,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) arguments->lv_refcount++; int dummy; - (void)call_func((uint8_t *)func, + (void)call_func((const char_u *)func, name_len, &rettv, 2, @@ -24455,7 +22567,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) NULL, NULL); - list_unref(arguments); + tv_list_unref(arguments); // Restore caller scope information restore_funccal(provider_caller_scope.funccalp); provider_caller_scope = saved_provider_caller_scope; @@ -24464,7 +22576,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) return rettv; } -bool eval_has_provider(char *name) +bool eval_has_provider(const char *name) { #define check_provider(name) \ if (has_##name == -1) { \ @@ -24498,91 +22610,3 @@ bool eval_has_provider(char *name) return false; } - -// Compute the `DictWatcher` address from a QUEUE node. This only exists for -// .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer arithmetic). -static DictWatcher *dictwatcher_node_data(QUEUE *q) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET -{ - return QUEUE_DATA(q, DictWatcher, node); -} - -// Send a change notification to all `dict` watchers that match `key`. -static void dictwatcher_notify(dict_T *dict, const char *key, typval_T *newtv, - typval_T *oldtv) - FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) -{ - typval_T argv[4]; - for (size_t i = 0; i < ARRAY_SIZE(argv); i++) { - init_tv(argv + i); - } - - argv[0].v_type = VAR_DICT; - argv[0].vval.v_dict = dict; - argv[1].v_type = VAR_STRING; - argv[1].vval.v_string = (char_u *)xstrdup(key); - argv[2].v_type = VAR_DICT; - argv[2].vval.v_dict = dict_alloc(); - argv[2].vval.v_dict->dv_refcount++; - - if (newtv) { - dictitem_T *v = dictitem_alloc((char_u *)"new"); - copy_tv(newtv, &v->di_tv); - dict_add(argv[2].vval.v_dict, v); - } - - if (oldtv) { - dictitem_T *v = dictitem_alloc((char_u *)"old"); - copy_tv(oldtv, &v->di_tv); - dict_add(argv[2].vval.v_dict, v); - } - - typval_T rettv; - - QUEUE *w; - QUEUE_FOREACH(w, &dict->watchers) { - DictWatcher *watcher = dictwatcher_node_data(w); - if (!watcher->busy && dictwatcher_matches(watcher, key)) { - init_tv(&rettv); - watcher->busy = true; - callback_call(&watcher->callback, 3, argv, &rettv); - watcher->busy = false; - clear_tv(&rettv); - } - } - - for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { - clear_tv(argv + i); - } -} - -// Test if `key` matches with with `watcher->key_pattern` -static bool dictwatcher_matches(DictWatcher *watcher, const char *key) - FUNC_ATTR_NONNULL_ALL -{ - // For now only allow very simple globbing in key patterns: a '*' at the end - // of the string means it should match everything up to the '*' instead of the - // whole string. - char *nul = strchr(watcher->key_pattern, NUL); - size_t len = nul - watcher->key_pattern; - if (*(nul - 1) == '*') { - return !strncmp(key, watcher->key_pattern, len - 1); - } else { - return !strcmp(key, watcher->key_pattern); - } -} - -// Perform all necessary cleanup for a `DictWatcher` instance. -static void dictwatcher_free(DictWatcher *watcher) - FUNC_ATTR_NONNULL_ALL -{ - callback_free(&watcher->callback); - xfree(watcher->key_pattern); - xfree(watcher); -} - -// Check if `d` has at least one watcher. -static bool is_watched(dict_T *d) -{ - return d && !QUEUE_EMPTY(&d->watchers); -} diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 57fee5c5a2..070bc35bd5 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -1,21 +1,23 @@ #ifndef NVIM_EVAL_H #define NVIM_EVAL_H -#include "nvim/profile.h" #include "nvim/hashtab.h" // For hashtab_T -#include "nvim/garray.h" // For garray_T -#include "nvim/buffer_defs.h" // For scid_T +#include "nvim/buffer_defs.h" #include "nvim/ex_cmds_defs.h" // For exarg_T +#include "nvim/eval/typval.h" +#include "nvim/profile.h" +#include "nvim/garray.h" #define COPYID_INC 2 #define COPYID_MASK (~0x1) // All user-defined functions are found in this hashtable. extern hashtab_T func_hashtab; + // From user function to hashitem and back. EXTERN ufunc_T dumuf; #define UF2HIKEY(fp) ((fp)->uf_name) -#define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf))) +#define HIKEY2UF(p) ((ufunc_T *)(p - offsetof(ufunc_T, uf_name))) #define HI2UF(hi) HIKEY2UF((hi)->hi_key) /// Defines for Vim variables diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 43e9f76c0f..fb31a65971 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -2,10 +2,11 @@ #include <msgpack.h> -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/ascii.h" +#include "nvim/macros.h" #include "nvim/message.h" #include "nvim/charset.h" // vim_str2nr #include "nvim/lib/kvec.h" @@ -51,16 +52,16 @@ static inline void create_special_dict(typval_T *const rettv, typval_T val) FUNC_ATTR_NONNULL_ALL { - dict_T *const dict = dict_alloc(); - dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); + dict_T *const dict = tv_dict_alloc(); + dictitem_T *const type_di = tv_dict_item_alloc_len(S_LEN("_TYPE")); type_di->di_tv.v_type = VAR_LIST; type_di->di_tv.v_lock = VAR_UNLOCKED; type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type]; type_di->di_tv.vval.v_list->lv_refcount++; - dict_add(dict, type_di); - dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); + tv_dict_add(dict, type_di); + dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL")); val_di->di_tv = val; - dict_add(dict, val_di); + tv_dict_add(dict, val_di); dict->dv_refcount++; *rettv = (typval_T) { .v_type = VAR_DICT, @@ -118,18 +119,18 @@ static inline int json_decoder_pop(ValuesStackItem obj, if (last_container.container.vval.v_list->lv_len != 0 && !obj.didcomma) { EMSG2(_("E474: Expected comma before list item: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } assert(last_container.special_val == NULL); - listitem_T *obj_li = listitem_alloc(); + listitem_T *obj_li = tv_list_item_alloc(); obj_li->li_tv = obj.val; - list_append(last_container.container.vval.v_list, obj_li); + tv_list_append(last_container.container.vval.v_list, obj_li); } else if (last_container.stack_index == kv_size(*stack) - 2) { if (!obj.didcolon) { EMSG2(_("E474: Expected colon before dictionary value: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } ValuesStackItem key = kv_pop(*stack); @@ -138,34 +139,35 @@ static inline int json_decoder_pop(ValuesStackItem obj, assert(!(key.is_special_string || key.val.vval.v_string == NULL || *key.val.vval.v_string == NUL)); - dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string); - clear_tv(&key.val); - if (dict_add(last_container.container.vval.v_dict, obj_di) + dictitem_T *const obj_di = tv_dict_item_alloc( + (const char *)key.val.vval.v_string); + tv_clear(&key.val); + if (tv_dict_add(last_container.container.vval.v_dict, obj_di) == FAIL) { assert(false); } obj_di->di_tv = obj.val; } else { - list_T *const kv_pair = list_alloc(); - list_append_list(last_container.special_val, kv_pair); - listitem_T *const key_li = listitem_alloc(); + list_T *const kv_pair = tv_list_alloc(); + tv_list_append_list(last_container.special_val, kv_pair); + listitem_T *const key_li = tv_list_item_alloc(); key_li->li_tv = key.val; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); + tv_list_append(kv_pair, key_li); + listitem_T *const val_li = tv_list_item_alloc(); val_li->li_tv = obj.val; - list_append(kv_pair, val_li); + tv_list_append(kv_pair, val_li); } } else { // Object with key only if (!obj.is_special_string && obj.val.v_type != VAR_STRING) { EMSG2(_("E474: Expected string key: %s"), *pp); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } else if (!obj.didcomma && (last_container.special_val == NULL && (DICT_LEN(last_container.container.vval.v_dict) != 0))) { EMSG2(_("E474: Expected comma before dictionary key: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } // Handle empty key and key represented as special dictionary @@ -173,16 +175,16 @@ static inline int json_decoder_pop(ValuesStackItem obj, && (obj.is_special_string || obj.val.vval.v_string == NULL || *obj.val.vval.v_string == NUL - || dict_find(last_container.container.vval.v_dict, - obj.val.vval.v_string, -1))) { - clear_tv(&obj.val); + || tv_dict_find(last_container.container.vval.v_dict, + (const char *)obj.val.vval.v_string, -1))) { + tv_clear(&obj.val); // Restart (void) kv_pop(*container_stack); ValuesStackItem last_container_val = kv_A(*stack, last_container.stack_index); while (kv_size(*stack) > last_container.stack_index) { - clear_tv(&(kv_pop(*stack).val)); + tv_clear(&(kv_pop(*stack).val)); } *pp = last_container.s; *didcomma = last_container_val.didcomma; @@ -430,7 +432,7 @@ static inline int parse_json_string(vimconv_T *const conv, } if (hasnul) { typval_T obj; - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(&obj, kMPString, ((typval_T) { .v_type = VAR_LIST, @@ -439,7 +441,7 @@ static inline int parse_json_string(vimconv_T *const conv, })); if (encode_list_write((void *) list, str, (size_t) (str_end - str)) == -1) { - clear_tv(&obj); + tv_clear(&obj); goto parse_json_string_fail; } xfree(str); @@ -806,7 +808,7 @@ json_decode_string_cycle_start: break; } case '[': { - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); list->lv_refcount++; typval_T tv = { .v_type = VAR_LIST, @@ -827,7 +829,7 @@ json_decode_string_cycle_start: list_T *val_list = NULL; if (next_map_special) { next_map_special = false; - val_list = list_alloc(); + val_list = tv_list_alloc(); val_list->lv_refcount++; create_special_dict(&tv, kMPMap, ((typval_T) { .v_type = VAR_LIST, @@ -835,7 +837,7 @@ json_decode_string_cycle_start: .vval = { .v_list = val_list }, })); } else { - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); dict->dv_refcount++; tv = (typval_T) { .v_type = VAR_DICT, @@ -887,7 +889,7 @@ json_decode_string_after_cycle: json_decode_string_fail: ret = FAIL; while (kv_size(stack)) { - clear_tv(&(kv_pop(stack).val)); + tv_clear(&(kv_pop(stack).val)); } json_decode_string_ret: kv_destroy(stack); @@ -933,7 +935,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.u64 }, }; } else { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, @@ -941,10 +943,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_list = list }, })); uint64_t n = mobj.via.u64; - list_append_number(list, 1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + tv_list_append_number(list, 1); + tv_list_append_number(list, (varnumber_T)((n >> 62) & 0x3)); + tv_list_append_number(list, (varnumber_T)((n >> 31) & 0x7FFFFFFF)); + tv_list_append_number(list, (varnumber_T)(n & 0x7FFFFFFF)); } break; } @@ -956,22 +958,28 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.i64 }, }; } else { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, .vval = { .v_list = list }, })); - uint64_t n = -((uint64_t) mobj.via.i64); - list_append_number(list, -1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + uint64_t n = -((uint64_t)mobj.via.i64); + tv_list_append_number(list, -1); + tv_list_append_number(list, (varnumber_T)((n >> 62) & 0x3)); + tv_list_append_number(list, (varnumber_T)((n >> 31) & 0x7FFFFFFF)); + tv_list_append_number(list, (varnumber_T)(n & 0x7FFFFFFF)); } break; } - case MSGPACK_OBJECT_FLOAT: { +#ifdef NVIM_MSGPACK_HAS_FLOAT32 + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: +#else + case MSGPACK_OBJECT_FLOAT: +#endif + { *rettv = (typval_T) { .v_type = VAR_FLOAT, .v_lock = VAR_UNLOCKED, @@ -980,7 +988,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_STR: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPString, ((typval_T) { .v_type = VAR_LIST, @@ -1002,7 +1010,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) }; break; } - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPBinary, ((typval_T) { .v_type = VAR_LIST, @@ -1016,7 +1024,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_ARRAY: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; *rettv = (typval_T) { .v_type = VAR_LIST, @@ -1024,9 +1032,9 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_list = list }, }; for (size_t i = 0; i < mobj.via.array.size; i++) { - listitem_T *const li = listitem_alloc(); + listitem_T *const li = tv_list_item_alloc(); li->li_tv.v_type = VAR_UNKNOWN; - list_append(list, li); + tv_list_append(list, li); if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { return FAIL; } @@ -1042,7 +1050,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) goto msgpack_to_vim_generic_map; } } - dict_T *const dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict->dv_refcount++; *rettv = (typval_T) { .v_type = VAR_DICT, @@ -1055,9 +1063,9 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr, mobj.via.map.ptr[i].key.via.str.size); di->di_tv.v_type = VAR_UNKNOWN; - if (dict_add(dict, di) == FAIL) { + if (tv_dict_add(dict, di) == FAIL) { // Duplicate key: fallback to generic map - clear_tv(rettv); + tv_clear(rettv); xfree(di); goto msgpack_to_vim_generic_map; } @@ -1067,7 +1075,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } break; msgpack_to_vim_generic_map: {} - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPMap, ((typval_T) { .v_type = VAR_LIST, @@ -1075,14 +1083,14 @@ msgpack_to_vim_generic_map: {} .vval = { .v_list = list }, })); for (size_t i = 0; i < mobj.via.map.size; i++) { - list_T *const kv_pair = list_alloc(); - list_append_list(list, kv_pair); - listitem_T *const key_li = listitem_alloc(); + list_T *const kv_pair = tv_list_alloc(); + tv_list_append_list(list, kv_pair); + listitem_T *const key_li = tv_list_item_alloc(); key_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); + tv_list_append(kv_pair, key_li); + listitem_T *const val_li = tv_list_item_alloc(); val_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, val_li); + tv_list_append(kv_pair, val_li); if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { return FAIL; } @@ -1093,11 +1101,11 @@ msgpack_to_vim_generic_map: {} break; } case MSGPACK_OBJECT_EXT: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; - list_append_number(list, mobj.via.ext.type); - list_T *const ext_val_list = list_alloc(); - list_append_list(list, ext_val_list); + tv_list_append_number(list, mobj.via.ext.type); + list_T *const ext_val_list = tv_list_alloc(); + tv_list_append_list(list, ext_val_list); create_special_dict(rettv, kMPExt, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h index 5c25a64f7a..c8e7a189e3 100644 --- a/src/nvim/eval/decode.h +++ b/src/nvim/eval/decode.h @@ -5,7 +5,7 @@ #include <msgpack.h> -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/decode.h.generated.h" diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index ee66b7cf09..26f9aaa27d 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -13,7 +13,7 @@ #include "nvim/eval/encode.h" #include "nvim/buffer_defs.h" // vimconv_T #include "nvim/eval.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/garray.h" #include "nvim/mbyte.h" #include "nvim/message.h" @@ -45,7 +45,8 @@ const char *const encode_special_var_names[] = { #endif /// Msgpack callback for writing to readfile()-style list -int encode_list_write(void *data, const char *buf, size_t len) +int encode_list_write(void *const data, const char *const buf, const size_t len) + FUNC_ATTR_NONNULL_ARG(1) { if (len == 0) { return 0; @@ -80,11 +81,11 @@ int encode_list_write(void *data, const char *buf, size_t len) str = xmemdupz(line_start, line_length); memchrsub(str, NUL, NL, line_length); } - list_append_allocated_string(list, str); + tv_list_append_allocated_string(list, str); line_end++; } if (line_end == end) { - list_append_allocated_string(list, NULL); + tv_list_append_allocated_string(list, NULL); } return 0; } @@ -743,11 +744,11 @@ bool encode_check_json_key(const typval_T *const tv) } const dictitem_T *type_di; const dictitem_T *val_di; - if ((type_di = dict_find((dict_T *) spdict, (char_u *) "_TYPE", -1)) == NULL + if ((type_di = tv_dict_find(spdict, S_LEN("_TYPE"))) == NULL || type_di->di_tv.v_type != VAR_LIST || (type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPString] && type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPBinary]) - || (val_di = dict_find((dict_T *) spdict, (char_u *) "_VAL", -1)) == NULL + || (val_di = tv_dict_find(spdict, S_LEN("_VAL"))) == NULL || val_di->di_tv.v_type != VAR_LIST) { return false; } diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c new file mode 100644 index 0000000000..ec6c86ac64 --- /dev/null +++ b/src/nvim/eval/executor.c @@ -0,0 +1,115 @@ +#include "nvim/eval/typval.h" +#include "nvim/eval/executor.h" +#include "nvim/eval.h" +#include "nvim/message.h" +#include "nvim/vim.h" +#include "nvim/globals.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/executor.c.generated.h" +#endif + +static char *e_letwrong = N_("E734: Wrong variable type for %s="); + +char *e_listidx = N_("E684: list index out of range: %" PRId64); + +/// Hanle tv1 += tv2, -=, .= +/// +/// @param[in,out] tv1 First operand, modified typval. +/// @param[in] tv2 Second operand. +/// @param[in] op Used operator. +/// +/// @return OK or FAIL. +int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, + const char *const op) + FUNC_ATTR_NONNULL_ALL +{ + // Can't do anything with a Funcref, a Dict or special value on the right. + if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { + switch (tv1->v_type) { + case VAR_DICT: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_SPECIAL: { + break; + } + case VAR_LIST: { + if (*op != '+' || tv2->v_type != VAR_LIST) { + break; + } + // List += List + if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) { + tv_list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); + } + return OK; + } + case VAR_NUMBER: + case VAR_STRING: { + if (tv2->v_type == VAR_LIST) { + break; + } + if (*op == '+' || *op == '-') { + // nr += nr or nr -= nr + varnumber_T n = tv_get_number(tv1); + if (tv2->v_type == VAR_FLOAT) { + float_T f = n; + + if (*op == '+') { + f += tv2->vval.v_float; + } else { + f -= tv2->vval.v_float; + } + tv_clear(tv1); + tv1->v_type = VAR_FLOAT; + tv1->vval.v_float = f; + } else { + if (*op == '+') { + n += tv_get_number(tv2); + } else { + n -= tv_get_number(tv2); + } + tv_clear(tv1); + tv1->v_type = VAR_NUMBER; + tv1->vval.v_number = n; + } + } else { + // str .= str + if (tv2->v_type == VAR_FLOAT) { + break; + } + const char *tvs = tv_get_string(tv1); + char numbuf[NUMBUFLEN]; + char *const s = (char *)concat_str( + (const char_u *)tvs, (const char_u *)tv_get_string_buf(tv2, + numbuf)); + tv_clear(tv1); + tv1->v_type = VAR_STRING; + tv1->vval.v_string = (char_u *)s; + } + return OK; + } + case VAR_FLOAT: { + if (*op == '.' || (tv2->v_type != VAR_FLOAT + && tv2->v_type != VAR_NUMBER + && tv2->v_type != VAR_STRING)) { + break; + } + const float_T f = (tv2->v_type == VAR_FLOAT + ? tv2->vval.v_float + : tv_get_number(tv2)); + if (*op == '+') { + tv1->vval.v_float += f; + } else { + tv1->vval.v_float -= f; + } + return OK; + } + case VAR_UNKNOWN: { + assert(false); + } + } + } + + EMSG2(_(e_letwrong), op); + return FAIL; +} diff --git a/src/nvim/eval/executor.h b/src/nvim/eval/executor.h new file mode 100644 index 0000000000..3d789f76a5 --- /dev/null +++ b/src/nvim/eval/executor.h @@ -0,0 +1,11 @@ +#ifndef NVIM_EVAL_EXECUTOR_H +#define NVIM_EVAL_EXECUTOR_H + +#include "nvim/eval/typval.h" + +extern char *e_listidx; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/executor.h.generated.h" +#endif +#endif // NVIM_EVAL_EXECUTOR_H diff --git a/src/nvim/eval/gc.c b/src/nvim/eval/gc.c new file mode 100644 index 0000000000..5ce52ddd70 --- /dev/null +++ b/src/nvim/eval/gc.c @@ -0,0 +1,11 @@ +#include "nvim/eval/typval.h" +#include "nvim/eval/gc.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/gc.c.generated.h" +#endif + +/// Head of list of all dictionaries +dict_T *gc_first_dict = NULL; +/// Head of list of all lists +list_T *gc_first_list = NULL; diff --git a/src/nvim/eval/gc.h b/src/nvim/eval/gc.h new file mode 100644 index 0000000000..c2e862e469 --- /dev/null +++ b/src/nvim/eval/gc.h @@ -0,0 +1,12 @@ +#ifndef NVIM_EVAL_GC_H +#define NVIM_EVAL_GC_H + +#include "nvim/eval/typval.h" + +extern dict_T *gc_first_dict; +extern list_T *gc_first_list; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/gc.h.generated.h" +#endif +#endif // NVIM_EVAL_GC_H diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c new file mode 100644 index 0000000000..779bb18175 --- /dev/null +++ b/src/nvim/eval/typval.c @@ -0,0 +1,2556 @@ +#include <stdio.h> +#include <stddef.h> +#include <string.h> +#include <assert.h> +#include <stdbool.h> + +#include "nvim/lib/queue.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/gc.h" +#include "nvim/eval/executor.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/typval_encode.h" +#include "nvim/eval.h" +#include "nvim/types.h" +#include "nvim/assert.h" +#include "nvim/memory.h" +#include "nvim/globals.h" +#include "nvim/hashtab.h" +#include "nvim/vim.h" +#include "nvim/ascii.h" +#include "nvim/pos.h" +#include "nvim/charset.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" +#include "nvim/macros.h" +#include "nvim/mbyte.h" +#include "nvim/message.h" +// TODO(ZyX-I): Move line_breakcheck out of misc1 +#include "nvim/misc1.h" // For line_breakcheck + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/typval.c.generated.h" +#endif + +bool tv_in_free_unref_items = false; + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead + +#define DICT_MAXNEST 100 + +const char *const tv_empty_string = ""; + +//{{{1 Lists +//{{{2 List item + +/// Allocate a list item +/// +/// @warning Allocated item is not initialized, do not forget to initialize it +/// and specifically set lv_lock. +/// +/// @return [allocated] new list item. +listitem_T *tv_list_item_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC +{ + return xmalloc(sizeof(listitem_T)); +} + +/// Free a list item +/// +/// Also clears the value. Does not touch watchers. +/// +/// @param[out] item Item to free. +void tv_list_item_free(listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_clear(&item->li_tv); + xfree(item); +} + +/// Remove a list item from a List and free it +/// +/// Also clears the value. +/// +/// @param[out] l List to remove item from. +/// @param[in,out] item Item to remove. +void tv_list_item_remove(list_T *const l, listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_list_remove_items(l, item, item); + tv_list_item_free(item); +} + +//{{{2 List watchers + +/// Add a watcher to a list +/// +/// @param[out] l List to add watcher to. +/// @param[in] lw Watcher to add. +void tv_list_watch_add(list_T *const l, listwatch_T *const lw) + FUNC_ATTR_NONNULL_ALL +{ + lw->lw_next = l->lv_watch; + l->lv_watch = lw; +} + +/// Remove a watcher from a list +/// +/// Does not give a warning if watcher was not found. +/// +/// @param[out] l List to remove watcher from. +/// @param[in] lwrem Watcher to remove. +void tv_list_watch_remove(list_T *const l, listwatch_T *const lwrem) + FUNC_ATTR_NONNULL_ALL +{ + listwatch_T **lwp = &l->lv_watch; + for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { + if (lw == lwrem) { + *lwp = lw->lw_next; + break; + } + lwp = &lw->lw_next; + } +} + +/// Advance watchers to the next item +/// +/// Used just before removing an item from a list. +/// +/// @param[out] l List from which item is removed. +/// @param[in] item List item being removed. +void tv_list_watch_fix(list_T *const l, const listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { + if (lw->lw_item == item) { + lw->lw_item = item->li_next; + } + } +} + +//{{{2 Alloc/free + +/// Allocate an empty list +/// +/// Caller should take care of the reference count. +/// +/// @return [allocated] new list. +list_T *tv_list_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC +{ + list_T *const list = xcalloc(1, sizeof(list_T)); + + // Prepend the list to the list of lists for garbage collection. + if (gc_first_list != NULL) { + gc_first_list->lv_used_prev = list; + } + list->lv_used_prev = NULL; + list->lv_used_next = gc_first_list; + gc_first_list = list; + return list; +} + +/// Free items contained in a list +/// +/// @param[in,out] l List to clear. +void tv_list_free_contents(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) { + // Remove the item before deleting it. + l->lv_first = item->li_next; + tv_clear(&item->li_tv); + xfree(item); + } + l->lv_len = 0; + l->lv_idx_item = NULL; + l->lv_last = NULL; + assert(l->lv_watch == NULL); +} + +/// Free a list itself, ignoring items it contains +/// +/// Ignores the reference count. +/// +/// @param[in,out] l List to free. +void tv_list_free_list(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + // Remove the list from the list of lists for garbage collection. + if (l->lv_used_prev == NULL) { + gc_first_list = l->lv_used_next; + } else { + l->lv_used_prev->lv_used_next = l->lv_used_next; + } + if (l->lv_used_next != NULL) { + l->lv_used_next->lv_used_prev = l->lv_used_prev; + } + + xfree(l); +} + +/// Free a list, including all items it points to +/// +/// Ignores the reference count. Does not do anything if +/// tv_in_free_unref_items is true. +/// +/// @param[in,out] l List to free. +void tv_list_free(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + if (!tv_in_free_unref_items) { + tv_list_free_contents(l); + tv_list_free_list(l); + } +} + +/// Unreference a list +/// +/// Decrements the reference count and frees when it becomes zero or less. +/// +/// @param[in,out] l List to unreference. +void tv_list_unref(list_T *const l) +{ + if (l != NULL && --l->lv_refcount <= 0) { + tv_list_free(l); + } +} + +//{{{2 Add/remove + +/// Remove items "item" to "item2" from list "l". +/// +/// @warning Does not free the listitem or the value! +/// +/// @param[out] l List to remove from. +/// @param[in] item First item to remove. +/// @param[in] item2 Last item to remove. +void tv_list_remove_items(list_T *const l, listitem_T *const item, + listitem_T *const item2) + FUNC_ATTR_NONNULL_ALL +{ + // Notify watchers. + for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) { + l->lv_len--; + tv_list_watch_fix(l, ip); + } + + if (item2->li_next == NULL) { + l->lv_last = item->li_prev; + } else { + item2->li_next->li_prev = item->li_prev; + } + if (item->li_prev == NULL) { + l->lv_first = item2->li_next; + } else { + item->li_prev->li_next = item2->li_next; + } + l->lv_idx_item = NULL; +} + +/// Insert list item +/// +/// @param[out] l List to insert to. +/// @param[in,out] ni Item to insert. +/// @param[in] item Item to insert before. If NULL, inserts at the end of the +/// list. +void tv_list_insert(list_T *const l, listitem_T *const ni, + listitem_T *const item) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + if (item == NULL) { + // Append new item at end of list. + tv_list_append(l, ni); + } else { + // Insert new item before existing item. + ni->li_prev = item->li_prev; + ni->li_next = item; + if (item->li_prev == NULL) { + l->lv_first = ni; + l->lv_idx++; + } else { + item->li_prev->li_next = ni; + l->lv_idx_item = NULL; + } + item->li_prev = ni; + l->lv_len++; + } +} + +/// Insert VimL value into a list +/// +/// @param[out] l List to insert to. +/// @param[in,out] tv Value to insert. Is copied (@see tv_copy()) to an +/// allocated listitem_T and inserted. +/// @param[in] item Item to insert before. If NULL, inserts at the end of the +/// list. +void tv_list_insert_tv(list_T *const l, typval_T *const tv, + listitem_T *const item) +{ + listitem_T *const ni = tv_list_item_alloc(); + + tv_copy(tv, &ni->li_tv); + tv_list_insert(l, ni, item); +} + +/// Append item to the end of list +/// +/// @param[out] l List to append to. +/// @param[in,out] item Item to append. +void tv_list_append(list_T *const l, listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + if (l->lv_last == NULL) { + // empty list + l->lv_first = item; + l->lv_last = item; + item->li_prev = NULL; + } else { + l->lv_last->li_next = item; + item->li_prev = l->lv_last; + l->lv_last = item; + } + l->lv_len++; + item->li_next = NULL; +} + +/// Append VimL value to the end of list +/// +/// @param[out] l List to append to. +/// @param[in,out] tv Value to append. Is copied (@see tv_copy()) to an +/// allocated listitem_T. +void tv_list_append_tv(list_T *const l, typval_T *const tv) + FUNC_ATTR_NONNULL_ALL +{ + listitem_T *const li = tv_list_item_alloc(); + tv_copy(tv, &li->li_tv); + tv_list_append(l, li); +} + +/// Append a list to a list as one item +/// +/// @param[out] l List to append to. +/// @param[in,out] itemlist List to append. Reference count is increased. +void tv_list_append_list(list_T *const list, list_T *const itemlist) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_list = itemlist; + tv_list_append(list, li); + if (itemlist != NULL) { + itemlist->lv_refcount++; + } +} + +/// Append a dictionary to a list +/// +/// @param[out] l List to append to. +/// @param[in,out] dict Dictionary to append. Reference count is increased. +void tv_list_append_dict(list_T *const list, dict_T *const dict) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + li->li_tv.v_type = VAR_DICT; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_dict = dict; + tv_list_append(list, li); + if (dict != NULL) { + dict->dv_refcount++; + } +} + +/// Make a copy of "str" and append it as an item to list "l" +/// +/// @param[out] l List to append to. +/// @param[in] str String to append. +/// @param[in] len Length of the appended string. May be -1, in this +/// case string is considered to be usual zero-terminated +/// string or NULL “empty” string. +void tv_list_append_string(list_T *const l, const char *const str, + const ptrdiff_t len) + FUNC_ATTR_NONNULL_ARG(1) +{ + if (str == NULL) { + assert(len == 0 || len == -1); + tv_list_append_allocated_string(l, NULL); + } else { + tv_list_append_allocated_string(l, (len >= 0 + ? xmemdupz(str, (size_t)len) + : xstrdup(str))); + } +} + +/// Append given string to the list +/// +/// Unlike list_append_string this function does not copy the string. +/// +/// @param[out] l List to append to. +/// @param[in] str String to append. +void tv_list_append_allocated_string(list_T *const l, char *const str) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + tv_list_append(l, li); + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_string = (char_u *)str; +} + +/// Append number to the list +/// +/// @param[out] l List to append to. +/// @param[in] n Number to append. Will be recorded in the allocated +/// listitem_T. +void tv_list_append_number(list_T *const l, const varnumber_T n) +{ + listitem_T *const li = tv_list_item_alloc(); + li->li_tv.v_type = VAR_NUMBER; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_number = n; + tv_list_append(l, li); +} + +//{{{2 Operations on the whole list + +/// Make a copy of list +/// +/// @param[in] conv If non-NULL, then all internal strings will be converted. +/// Only used when `deep` is true. +/// @param[in] orig Original list to copy. +/// @param[in] deep If false, then shallow copy will be done. +/// @param[in] copyID See var_item_copy(). +/// +/// @return Copied list. May be NULL in case original list is NULL or some +/// failure happens. The refcount of the new list is set to 1. +list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, + const bool deep, const int copyID) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (orig == NULL) { + return NULL; + } + + list_T *copy = tv_list_alloc(); + if (copyID != 0) { + // Do this before adding the items, because one of the items may + // refer back to this list. + orig->lv_copyID = copyID; + orig->lv_copylist = copy; + } + listitem_T *item; + for (item = orig->lv_first; item != NULL && !got_int; + item = item->li_next) { + listitem_T *const ni = tv_list_item_alloc(); + if (deep) { + if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { + xfree(ni); + break; + } + } else { + tv_copy(&item->li_tv, &ni->li_tv); + } + tv_list_append(copy, ni); + } + copy->lv_refcount++; + if (item != NULL) { + tv_list_unref(copy); + copy = NULL; + } + + return copy; +} + +/// Extend first list with the second +/// +/// @param[out] l1 List to extend. +/// @param[in] l2 List to extend with. +/// @param[in] bef If not NULL, extends before this item. +void tv_list_extend(list_T *const l1, list_T *const l2, + listitem_T *const bef) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + int todo = l2->lv_len; + listitem_T *const befbef = (bef == NULL ? NULL : bef->li_prev); + listitem_T *const saved_next = (befbef == NULL ? NULL : befbef->li_next); + // We also quit the loop when we have inserted the original item count of + // the list, avoid a hang when we extend a list with itself. + for (listitem_T *item = l2->lv_first + ; item != NULL && --todo >= 0 + ; item = (item == befbef ? saved_next : item->li_next)) { + tv_list_insert_tv(l1, &item->li_tv, bef); + } +} + +/// Concatenate lists into a new list +/// +/// @param[in] l1 First list. +/// @param[in] l2 Second list. +/// @param[out] ret_tv Location where new list is saved. +/// +/// @return OK or FAIL. +int tv_list_concat(list_T *const l1, list_T *const l2, typval_T *const tv) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + list_T *l; + + tv->v_type = VAR_LIST; + + if (l1 == NULL && l2 == NULL) { + l = NULL; + } else if (l1 == NULL) { + l = tv_list_copy(NULL, l2, false, 0); + } else { + l = tv_list_copy(NULL, l1, false, 0); + if (l != NULL && l2 != NULL) { + tv_list_extend(l, l2, NULL); + } + } + if (l == NULL && !(l1 == NULL && l2 == NULL)) { + return FAIL; + } + + tv->vval.v_list = l; + return OK; +} + +typedef struct { + char_u *s; + char_u *tofree; +} Join; + +/// Join list into a string, helper function +/// +/// @param[out] gap Garray where result will be saved. +/// @param[in] l List to join. +/// @param[in] sep Used separator. +/// @param[in] join_gap Garray to keep each list item string. +/// +/// @return OK in case of success, FAIL otherwise. +static int list_join_inner(garray_T *const gap, list_T *const l, + const char *const sep, garray_T *const join_gap) + FUNC_ATTR_NONNULL_ALL +{ + size_t sumlen = 0; + bool first = true; + listitem_T *item; + + // Stringify each item in the list. + for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { + char *s; + size_t len; + s = encode_tv2echo(&item->li_tv, &len); + if (s == NULL) { + return FAIL; + } + + sumlen += len; + + Join *const p = GA_APPEND_VIA_PTR(Join, join_gap); + p->tofree = p->s = (char_u *)s; + + line_breakcheck(); + } + + // Allocate result buffer with its total size, avoid re-allocation and + // multiple copy operations. Add 2 for a tailing ']' and NUL. + if (join_gap->ga_len >= 2) { + sumlen += strlen(sep) * (size_t)(join_gap->ga_len - 1); + } + ga_grow(gap, (int)sumlen + 2); + + for (int i = 0; i < join_gap->ga_len && !got_int; i++) { + if (first) { + first = false; + } else { + ga_concat(gap, (const char_u *)sep); + } + const Join *const p = ((const Join *)join_gap->ga_data) + i; + + if (p->s != NULL) { + ga_concat(gap, p->s); + } + line_breakcheck(); + } + + return OK; +} + +/// Join list into a string using given separator +/// +/// @param[out] gap Garray where result will be saved. +/// @param[in] l Joined list. +/// @param[in] sep Separator. +/// +/// @return OK in case of success, FAIL otherwise. +int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) + FUNC_ATTR_NONNULL_ALL +{ + if (l->lv_len < 1) { + return OK; + } + + garray_T join_ga; + int retval; + + ga_init(&join_ga, (int)sizeof(Join), l->lv_len); + retval = list_join_inner(gap, l, sep, &join_ga); + +#define FREE_JOIN_TOFREE(join) xfree((join)->tofree) + GA_DEEP_CLEAR(&join_ga, Join, FREE_JOIN_TOFREE); +#undef FREE_JOIN_TOFREE + + return retval; +} + +/// Chech whether two lists are equal +/// +/// @param[in] l1 First list to compare. +/// @param[in] l2 Second list to compare. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +/// +/// @return True if lists are equal, false otherwise. +bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, + const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (l1 == l2) { + return true; + } + if (l1 == NULL || l2 == NULL) { + return false; + } + if (tv_list_len(l1) != tv_list_len(l2)) { + return false; + } + + listitem_T *item1 = l1->lv_first; + listitem_T *item2 = l2->lv_first; + for (; item1 != NULL && item2 != NULL + ; item1 = item1->li_next, item2 = item2->li_next) { + if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) { + return false; + } + } + assert(item1 == NULL && item2 == NULL); + return true; +} + +//{{{2 Indexing/searching + +/// Locate item with a given index in a list and return it +/// +/// @param[in] l List to index. +/// @param[in] n Index. Negative index is counted from the end, -1 is the last +/// item. +/// +/// @return Item at the given index or NULL if `n` is out of range. +listitem_T *tv_list_find(list_T *const l, int n) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + STATIC_ASSERT(sizeof(n) == sizeof(l->lv_idx), + "n and lv_idx sizes do not match"); + if (l == NULL) { + return NULL; + } + + // Negative index is relative to the end. + if (n < 0) { + n = l->lv_len + n; + } + + // Check for index out of range. + if (n < 0 || n >= l->lv_len) { + return NULL; + } + + int idx; + listitem_T *item; + + // When there is a cached index may start search from there. + if (l->lv_idx_item != NULL) { + if (n < l->lv_idx / 2) { + // Closest to the start of the list. + item = l->lv_first; + idx = 0; + } else if (n > (l->lv_idx + l->lv_len) / 2) { + // Closest to the end of the list. + item = l->lv_last; + idx = l->lv_len - 1; + } else { + // Closest to the cached index. + item = l->lv_idx_item; + idx = l->lv_idx; + } + } else { + if (n < l->lv_len / 2) { + // Closest to the start of the list. + item = l->lv_first; + idx = 0; + } else { + // Closest to the end of the list. + item = l->lv_last; + idx = l->lv_len - 1; + } + } + + while (n > idx) { + // Search forward. + item = item->li_next; + idx++; + } + while (n < idx) { + // Search backward. + item = item->li_prev; + idx--; + } + + assert(idx == n); + // Cache the used index. + l->lv_idx = idx; + l->lv_idx_item = item; + + return item; +} + +/// Get list item l[n] as a number +/// +/// @param[in] l List to index. +/// @param[in] n Index in a list. +/// @param[out] ret_error Location where 1 will be saved if index was not +/// found. May be NULL. If everything is OK, +/// `*ret_error` is not touched. +/// +/// @return Integer value at the given index or -1. +varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *const ret_error) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + const listitem_T *const li = tv_list_find(l, n); + if (li == NULL) { + if (ret_error != NULL) { + *ret_error = true; + } + return -1; + } + return tv_get_number_chk(&li->li_tv, ret_error); +} + +/// Get list item l[n] as a string +/// +/// @param[in] l List to index. +/// @param[in] n Index in a list. +/// +/// @return List item string value or NULL in case of error. +const char *tv_list_find_str(list_T *const l, const int n) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + const listitem_T *const li = tv_list_find(l, n); + if (li == NULL) { + emsgf(_(e_listidx), (int64_t)n); + return NULL; + } + return tv_get_string(&li->li_tv); +} + +/// Locate item in a list and return its index +/// +/// @param[in] l List to search. +/// @param[in] item Item to search for. +/// +/// @return Index of an item or -1 if item is not in the list. +long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (l == NULL) { + return -1; + } + long idx = 0; + const listitem_T *li; + for (li = l->lv_first; li != NULL && li != item; li = li->li_next) { + idx++; + } + if (li == NULL) { + return -1; + } + return idx; +} + +//{{{1 Dictionaries +//{{{2 Dictionary watchers + +/// Perform all necessary cleanup for a `DictWatcher` instance +/// +/// @param watcher Watcher to free. +static void tv_dict_watcher_free(DictWatcher *watcher) + FUNC_ATTR_NONNULL_ALL +{ + callback_free(&watcher->callback); + xfree(watcher->key_pattern); + xfree(watcher); +} + +/// Add watcher to a dictionary +/// +/// @param[in] dict Dictionary to add watcher to. +/// @param[in] key_pattern Pattern to watch for. +/// @param[in] key_pattern_len Key pattern length. +/// @param callback Function to be called on events. +void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, + const size_t key_pattern_len, Callback callback) + FUNC_ATTR_NONNULL_ARG(2) +{ + if (dict == NULL) { + return; + } + DictWatcher *const watcher = xmalloc(sizeof(DictWatcher)); + watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); + watcher->key_pattern_len = key_pattern_len; + watcher->callback = callback; + watcher->busy = false; + QUEUE_INSERT_TAIL(&dict->watchers, &watcher->node); +} + +/// Check whether two callbacks are equal +/// +/// @param[in] cb1 First callback to check. +/// @param[in] cb2 Second callback to check. +/// +/// @return True if they are equal, false otherwise. +bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (cb1->type != cb2->type) { + return false; + } + switch (cb1->type) { + case kCallbackFuncref: { + return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0; + } + case kCallbackPartial: { + // FIXME: this is inconsistent with tv_equal but is needed for precision + // maybe change dictwatcheradd to return a watcher id instead? + return cb1->data.partial == cb2->data.partial; + } + case kCallbackNone: { + return true; + } + } + assert(false); + return false; +} + +/// Remove watcher from a dictionary +/// +/// @param dict Dictionary to remove watcher from. +/// @param[in] key_pattern Pattern to remove watcher for. +/// @param[in] key_pattern_len Pattern length. +/// @param callback Callback to remove watcher for. +/// +/// @return True on success, false if relevant watcher was not found. +bool tv_dict_watcher_remove(dict_T *const dict, const char *const key_pattern, + const size_t key_pattern_len, + Callback callback) + FUNC_ATTR_NONNULL_ARG(2) +{ + if (dict == NULL) { + return false; + } + + QUEUE *w = NULL; + DictWatcher *watcher = NULL; + bool matched = false; + QUEUE_FOREACH(w, &dict->watchers) { + watcher = tv_dict_watcher_node_data(w); + if (tv_callback_equal(&watcher->callback, &callback) + && watcher->key_pattern_len == key_pattern_len + && memcmp(watcher->key_pattern, key_pattern, key_pattern_len) == 0) { + matched = true; + break; + } + } + + if (!matched) { + return false; + } + + QUEUE_REMOVE(w); + tv_dict_watcher_free(watcher); + return true; +} + +/// Test if `key` matches with with `watcher->key_pattern` +/// +/// @param[in] watcher Watcher to check key pattern from. +/// @param[in] key Key to check. +/// +/// @return true if key matches, false otherwise. +static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + // For now only allow very simple globbing in key patterns: a '*' at the end + // of the string means it should match everything up to the '*' instead of the + // whole string. + const size_t len = watcher->key_pattern_len; + if (len && watcher->key_pattern[len - 1] == '*') { + return strncmp(key, watcher->key_pattern, len - 1) == 0; + } else { + return strcmp(key, watcher->key_pattern) == 0; + } +} + +/// Send a change notification to all dictionary watchers that match given key +/// +/// @param[in] dict Dictionary which was modified. +/// @param[in] key Key which was modified. +/// @param[in] newtv New key value. +/// @param[in] oldtv Old key value. +void tv_dict_watcher_notify(dict_T *const dict, const char *const key, + typval_T *const newtv, typval_T *const oldtv) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + typval_T argv[3]; + + argv[0].v_type = VAR_DICT; + argv[0].v_lock = VAR_UNLOCKED; + argv[0].vval.v_dict = dict; + argv[1].v_type = VAR_STRING; + argv[1].v_lock = VAR_UNLOCKED; + argv[1].vval.v_string = (char_u *)xstrdup(key); + argv[2].v_type = VAR_DICT; + argv[2].v_lock = VAR_UNLOCKED; + argv[2].vval.v_dict = tv_dict_alloc(); + argv[2].vval.v_dict->dv_refcount++; + + if (newtv) { + dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("new")); + tv_copy(newtv, &v->di_tv); + tv_dict_add(argv[2].vval.v_dict, v); + } + + if (oldtv) { + dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("old")); + tv_copy(oldtv, &v->di_tv); + tv_dict_add(argv[2].vval.v_dict, v); + } + + typval_T rettv; + + QUEUE *w; + QUEUE_FOREACH(w, &dict->watchers) { + DictWatcher *watcher = tv_dict_watcher_node_data(w); + if (!watcher->busy && tv_dict_watcher_matches(watcher, key)) { + rettv = TV_INITIAL_VALUE; + watcher->busy = true; + callback_call(&watcher->callback, 3, argv, &rettv); + watcher->busy = false; + tv_clear(&rettv); + } + } + + for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { + tv_clear(argv + i); + } +} + +//{{{2 Dictionary item + +/// Allocate a dictionary item +/// +/// @note that the value of the item (->di_tv) still needs to be initialized. +/// +/// @param[in] key Key, is copied to the new item. +/// @param[in] key_len Key length. +/// +/// @return [allocated] new dictionary item. +dictitem_T *tv_dict_item_alloc_len(const char *const key, const size_t key_len) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + dictitem_T *const di = xmalloc(offsetof(dictitem_T, di_key) + key_len + 1); + memcpy(di->di_key, key, key_len); + di->di_key[key_len] = NUL; + di->di_flags = DI_FLAGS_ALLOC; + return di; +} + +/// Allocate a dictionary item +/// +/// @note that the value of the item (->di_tv) still needs to be initialized. +/// +/// @param[in] key Key, is copied to the new item. +/// +/// @return [allocated] new dictionary item. +dictitem_T *tv_dict_item_alloc(const char *const key) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + return tv_dict_item_alloc_len(key, strlen(key)); +} + +/// Free a dictionary item, also clearing the value +/// +/// @param item Item to free. +void tv_dict_item_free(dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_clear(&item->di_tv); + if (item->di_flags & DI_FLAGS_ALLOC) { + xfree(item); + } +} + +/// Make a copy of a dictionary item +/// +/// @param[in] di Item to copy. +/// +/// @return [allocated] new dictionary item. +static dictitem_T *tv_dict_item_copy(dictitem_T *const di) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + dictitem_T *const new_di = tv_dict_item_alloc((const char *)di->di_key); + tv_copy(&di->di_tv, &new_di->di_tv); + return new_di; +} + +/// Remove item from dictionary and free it +/// +/// @param dict Dictionary to remove item from. +/// @param item Item to remove. +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); + if (HASHITEM_EMPTY(hi)) { + emsgf(_(e_intern2), "tv_dict_item_remove()"); + } else { + hash_remove(&dict->dv_hashtab, hi); + } + tv_dict_item_free(item); +} + +//{{{2 Alloc/free + +/// Allocate an empty dictionary +/// +/// @return [allocated] new dictionary. +dict_T *tv_dict_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + dict_T *const d = xmalloc(sizeof(dict_T)); + + // Add the dict to the list of dicts for garbage collection. + if (gc_first_dict != NULL) { + gc_first_dict->dv_used_prev = d; + } + d->dv_used_next = gc_first_dict; + d->dv_used_prev = NULL; + gc_first_dict = d; + + hash_init(&d->dv_hashtab); + d->dv_lock = VAR_UNLOCKED; + d->dv_scope = VAR_NO_SCOPE; + d->dv_refcount = 0; + d->dv_copyID = 0; + QUEUE_INIT(&d->watchers); + + return d; +} + +/// Free items contained in a dictionary +/// +/// @param[in,out] d Dictionary to clear. +void tv_dict_free_contents(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + // Lock the hashtab, we don't want it to resize while freeing items. + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + HASHTAB_ITER(&d->dv_hashtab, hi, { + // Remove the item before deleting it, just in case there is + // something recursive causing trouble. + dictitem_T *const di = TV_DICT_HI2DI(hi); + hash_remove(&d->dv_hashtab, hi); + tv_dict_item_free(di); + }); + + while (!QUEUE_EMPTY(&d->watchers)) { + QUEUE *w = QUEUE_HEAD(&d->watchers); + QUEUE_REMOVE(w); + DictWatcher *watcher = tv_dict_watcher_node_data(w); + tv_dict_watcher_free(watcher); + } + + hash_clear(&d->dv_hashtab); + d->dv_hashtab.ht_locked--; + hash_init(&d->dv_hashtab); +} + +/// Free a dictionary itself, ignoring items it contains +/// +/// Ignores the reference count. +/// +/// @param[in,out] d Dictionary to free. +void tv_dict_free_dict(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + // Remove the dict from the list of dicts for garbage collection. + if (d->dv_used_prev == NULL) { + gc_first_dict = d->dv_used_next; + } else { + d->dv_used_prev->dv_used_next = d->dv_used_next; + } + if (d->dv_used_next != NULL) { + d->dv_used_next->dv_used_prev = d->dv_used_prev; + } + + xfree(d); +} + +/// Free a dictionary, including all items it contains +/// +/// Ignores the reference count. +/// +/// @param d Dictionary to free. +void tv_dict_free(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + if (!tv_in_free_unref_items) { + tv_dict_free_contents(d); + tv_dict_free_dict(d); + } +} + + +/// Unreference a dictionary +/// +/// Decrements the reference count and frees dictionary when it becomes zero. +/// +/// @param[in] d Dictionary to operate on. +void tv_dict_unref(dict_T *const d) +{ + if (d != NULL && --d->dv_refcount <= 0) { + tv_dict_free(d); + } +} + +//{{{2 Indexing/searching + +/// Find item in dictionary +/// +/// @param[in] d Dictionary to check. +/// @param[in] key Dictionary key. +/// @param[in] len Key length. If negative, then strlen(key) is used. +/// +/// @return found item or NULL if nothing was found. +dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, + const ptrdiff_t len) + FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (d == NULL) { + return NULL; + } + hashitem_T *const hi = (len < 0 + ? hash_find(&d->dv_hashtab, (const char_u *)key) + : hash_find_len(&d->dv_hashtab, key, (size_t)len)); + if (HASHITEM_EMPTY(hi)) { + return NULL; + } + return TV_DICT_HI2DI(hi); +} + +/// Get a number item from a dictionary +/// +/// Returns 0 if the entry does not exist. +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Key to find in dictionary. +/// +/// @return Dictionary item. +varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + dictitem_T *const di = tv_dict_find(d, key, -1); + if (di == NULL) { + return 0; + } + return tv_get_number(&di->di_tv); +} + +/// Get a string item from a dictionary +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Dictionary key. +/// @param[in] save If true, returned string will be placed in the allocated +/// memory. +/// +/// @return NULL if key does not exist, empty string in case of type error, +/// string item value otherwise. If returned value is not NULL, it may +/// be allocated depending on `save` argument. +char *tv_dict_get_string(const dict_T *const d, const char *const key, + const bool save) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char numbuf[NUMBUFLEN]; + const char *const s = tv_dict_get_string_buf(d, key, numbuf); + if (save && s != NULL) { + return xstrdup(s); + } + return (char *)s; +} + +/// Get a string item from a dictionary +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Dictionary key. +/// @param[in] numbuf Numbuf for. +/// +/// @return NULL if key does not exist, empty string in case of type error, +/// string item value otherwise. +const char *tv_dict_get_string_buf(const dict_T *const d, const char *const key, + char *const numbuf) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + const dictitem_T *const di = tv_dict_find(d, key, -1); + if (di == NULL) { + return NULL; + } + return tv_get_string_buf(&di->di_tv, numbuf); +} + +/// Get a function from a dictionary +/// +/// @param[in] d Dictionary to get callback from. +/// @param[in] key Dictionary key. +/// @param[in] key_len Key length, may be -1 to use strlen(). +/// @param[out] result The address where a pointer to the wanted callback +/// will be left. +/// +/// @return true/false on success/failure. +bool tv_dict_get_callback(dict_T *const d, + const char *const key, const ptrdiff_t key_len, + Callback *const result) + FUNC_ATTR_NONNULL_ARG(2, 4) FUNC_ATTR_WARN_UNUSED_RESULT +{ + result->type = kCallbackNone; + + dictitem_T *const di = tv_dict_find(d, key, key_len); + + if (di == NULL) { + return true; + } + + if (!tv_is_func(di->di_tv) && di->di_tv.v_type != VAR_STRING) { + emsgf(_("E6000: Argument is not a function or function name")); + return false; + } + + typval_T tv; + tv_copy(&di->di_tv, &tv); + set_selfdict(&tv, d); + const bool res = callback_from_typval(result, &tv); + tv_clear(&tv); + return res; +} + +//{{{2 dict_add* + +/// Add item to dictionary +/// +/// @param[out] d Dictionary to add to. +/// @param[in] item Item to add. +/// +/// @return FAIL if key already exists. +int tv_dict_add(dict_T *const d, dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + return hash_add(&d->dv_hashtab, item->di_key); +} + +/// Add a list entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param list List to add. Will have reference count incremented. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_list(dict_T *const d, const char *const key, + const size_t key_len, list_T *const list) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_LIST; + item->di_tv.vval.v_list = list; + list->lv_refcount++; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + +/// Add a dictionary entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param dict Dictionary to add. Will have reference count incremented. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_dict(dict_T *const d, const char *const key, + const size_t key_len, dict_T *const dict) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_DICT; + item->di_tv.vval.v_dict = dict; + dict->dv_refcount++; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + +/// Add a number entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] nr Number to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_nr(dict_T *const d, const char *const key, + const size_t key_len, const varnumber_T nr) +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_NUMBER; + item->di_tv.vval.v_number = nr; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + +/// Add a string entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] val String to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_str(dict_T *const d, + const char *const key, const size_t key_len, + const char *const val) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_STRING; + item->di_tv.vval.v_string = (char_u *)xstrdup(val); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + +//{{{2 Operations on the whole dict + +/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. +/// +/// @param d The Dictionary to clear +void tv_dict_clear(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + + HASHTAB_ITER(&d->dv_hashtab, hi, { + tv_dict_item_free(TV_DICT_HI2DI(hi)); + hash_remove(&d->dv_hashtab, hi); + }); + + hash_unlock(&d->dv_hashtab); +} + +/// Extend dictionary with items from another dictionary +/// +/// @param d1 Dictionary to extend. +/// @param[in] d2 Dictionary to extend with. +/// @param[in] action "error", "force", "keep": +/// +/// e*, including "error": duplicate key gives an error. +/// f*, including "force": duplicate d2 keys override d1. +/// other, including "keep": duplicate d2 keys ignored. +void tv_dict_extend(dict_T *const d1, dict_T *const d2, + const char *const action) + FUNC_ATTR_NONNULL_ALL +{ + const bool watched = tv_dict_is_watched(d1); + const char *const arg_errmsg = _("extend() argument"); + const size_t arg_errmsg_len = strlen(arg_errmsg); + + TV_DICT_ITER(d2, di2, { + dictitem_T *const di1 = tv_dict_find(d1, (const char *)di2->di_key, -1); + if (d1->dv_scope != VAR_NO_SCOPE) { + // Disallow replacing a builtin function in l: and g:. + // Check the key to be valid when adding to any scope. + if (d1->dv_scope == VAR_DEF_SCOPE + && tv_is_func(di2->di_tv) + && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) { + break; + } + if (!valid_varname((const char *)di2->di_key)) { + break; + } + } + if (di1 == NULL) { + dictitem_T *const new_di = tv_dict_item_copy(di2); + if (tv_dict_add(d1, new_di) == FAIL) { + tv_dict_item_free(new_di); + } else if (watched) { + tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, + NULL); + } + } else if (*action == 'e') { + emsgf(_("E737: Key already exists: %s"), di2->di_key); + break; + } else if (*action == 'f' && di2 != di1) { + typval_T oldtv; + + if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) + || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { + break; + } + + if (watched) { + tv_copy(&di1->di_tv, &oldtv); + } + + tv_clear(&di1->di_tv); + tv_copy(&di2->di_tv, &di1->di_tv); + + if (watched) { + tv_dict_watcher_notify(d1, (const char *)di1->di_key, &di1->di_tv, + &oldtv); + tv_clear(&oldtv); + } + } + }); +} + +/// Compare two dictionaries +/// +/// @param[in] d1 First dictionary. +/// @param[in] d2 Second dictionary. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +bool tv_dict_equal(dict_T *const d1, dict_T *const d2, + const bool ic, const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (d1 == d2) { + return true; + } + if (d1 == NULL || d2 == NULL) { + return false; + } + if (tv_dict_len(d1) != tv_dict_len(d2)) { + return false; + } + + TV_DICT_ITER(d1, di1, { + dictitem_T *const di2 = tv_dict_find(d2, (const char *)di1->di_key, -1); + if (di2 == NULL) { + return false; + } + if (!tv_equal(&di1->di_tv, &di2->di_tv, ic, recursive)) { + return false; + } + }); + return true; +} + +/// Make a copy of dictionary +/// +/// @param[in] conv If non-NULL, then all internal strings will be converted. +/// @param[in] orig Original dictionary to copy. +/// @param[in] deep If false, then shallow copy will be done. +/// @param[in] copyID See var_item_copy(). +/// +/// @return Copied dictionary. May be NULL in case original dictionary is NULL +/// or some failure happens. The refcount of the new dictionary is set +/// to 1. +dict_T *tv_dict_copy(const vimconv_T *const conv, + dict_T *const orig, + const bool deep, + const int copyID) +{ + if (orig == NULL) { + return NULL; + } + + dict_T *copy = tv_dict_alloc(); + if (copyID != 0) { + orig->dv_copyID = copyID; + orig->dv_copydict = copy; + } + TV_DICT_ITER(orig, di, { + if (got_int) { + break; + } + dictitem_T *new_di; + if (conv == NULL || conv->vc_type == CONV_NONE) { + new_di = tv_dict_item_alloc((const char *)di->di_key); + } else { + size_t len = STRLEN(di->di_key); + char *const key = (char *)string_convert(conv, di->di_key, &len); + if (key == NULL) { + new_di = tv_dict_item_alloc_len((const char *)di->di_key, len); + } else { + new_di = tv_dict_item_alloc_len(key, len); + xfree(key); + } + } + if (deep) { + if (var_item_copy(conv, &di->di_tv, &new_di->di_tv, deep, + copyID) == FAIL) { + xfree(new_di); + break; + } + } else { + tv_copy(&di->di_tv, &new_di->di_tv); + } + if (tv_dict_add(copy, new_di) == FAIL) { + tv_dict_item_free(new_di); + break; + } + }); + + copy->dv_refcount++; + if (got_int) { + tv_dict_unref(copy); + copy = NULL; + } + + return copy; +} + +/// Set all existing keys in "dict" as read-only. +/// +/// This does not protect against adding new keys to the Dictionary. +/// +/// @param dict The dict whose keys should be frozen. +void tv_dict_set_keys_readonly(dict_T *const dict) + FUNC_ATTR_NONNULL_ALL +{ + TV_DICT_ITER(dict, di, { + di->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + }); +} + +//{{{1 Generic typval operations +//{{{2 Init/alloc/clear +//{{{3 Alloc + +/// Allocate an empty list for a return value +/// +/// Also sets reference count. +/// +/// @param[out] ret_tv Structure where list is saved. +/// +/// @return [allocated] pointer to the created list. +list_T *tv_list_alloc_ret(typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC +{ + list_T *const l = tv_list_alloc(); + ret_tv->vval.v_list = l; + ret_tv->v_type = VAR_LIST; + ret_tv->v_lock = VAR_UNLOCKED; + l->lv_refcount++; + return l; +} + +/// Allocate an empty dictionary for a return value +/// +/// Also sets reference count. +/// +/// @param[out] ret_tv Structure where dictionary is saved. +void tv_dict_alloc_ret(typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL +{ + dict_T *const d = tv_dict_alloc(); + ret_tv->vval.v_dict = d; + ret_tv->v_type = VAR_DICT; + ret_tv->v_lock = VAR_UNLOCKED; + d->dv_refcount++; +} + +//{{{3 Clear +#define TYPVAL_ENCODE_ALLOW_SPECIALS false + +#define TYPVAL_ENCODE_CONV_NIL(tv) \ + do { \ + tv->vval.v_special = kSpecialVarFalse; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ + TYPVAL_ENCODE_CONV_NIL(tv) + +#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ + do { \ + (void)num; \ + tv->vval.v_number = 0; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) + +#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ + do { \ + tv->vval.v_float = 0; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \ + do { \ + xfree(buf); \ + tv->vval.v_string = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) + +#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) + +static inline int _nothing_conv_func_start(typval_T *const tv, + char_u *const fun) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) +{ + tv->v_lock = VAR_UNLOCKED; + if (tv->v_type == VAR_PARTIAL) { + partial_T *const pt_ = tv->vval.v_partial; + if (pt_ != NULL && pt_->pt_refcount > 1) { + pt_->pt_refcount--; + tv->vval.v_partial = NULL; + return OK; + } + } else { + func_unref(fun); + if ((const char *)fun != tv_empty_string) { + xfree(fun); + } + tv->vval.v_string = NULL; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ + do { \ + if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \ + return OK; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) + +static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + if (tv->v_type == VAR_PARTIAL) { + partial_T *const pt = tv->vval.v_partial; + if (pt == NULL) { + return; + } + // Dictionary should already be freed by the time. + // If it was not freed then it is a part of the reference cycle. + assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID); + pt->pt_dict = NULL; + // As well as all arguments. + pt->pt_argc = 0; + assert(pt->pt_refcount <= 1); + partial_unref(pt); + tv->vval.v_partial = NULL; + assert(tv->v_lock == VAR_UNLOCKED); + } +} +#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID) + +#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ + do { \ + tv_list_unref(tv->vval.v_list); \ + tv->vval.v_list = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ + do { \ + assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ + tv_dict_unref((dict_T *)dict); \ + *((dict_T **)&dict) = NULL; \ + if (tv != NULL) { \ + ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ + } \ + } while (0) + +static inline int _nothing_conv_real_list_after_start( + typval_T *const tv, MPConvStackVal *const mpsv) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT +{ + assert(tv != NULL); + tv->v_lock = VAR_UNLOCKED; + if (tv->vval.v_list->lv_refcount > 1) { + tv->vval.v_list->lv_refcount--; + tv->vval.v_list = NULL; + mpsv->data.l.li = NULL; + return OK; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) + +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \ + do { \ + if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \ + goto typval_encode_stop_converting_one_item; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) + +static inline void _nothing_conv_list_end(typval_T *const tv) + FUNC_ATTR_ALWAYS_INLINE +{ + if (tv == NULL) { + return; + } + assert(tv->v_type == VAR_LIST); + list_T *const list = tv->vval.v_list; + tv_list_unref(list); + tv->vval.v_list = NULL; +} +#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv) + +static inline int _nothing_conv_real_dict_after_start( + typval_T *const tv, dict_T **const dictp, const void *const nodictvar, + MPConvStackVal *const mpsv) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (tv != NULL) { + tv->v_lock = VAR_UNLOCKED; + } + if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) { + (*dictp)->dv_refcount--; + *dictp = NULL; + mpsv->data.d.todo = 0; + return OK; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) + +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \ + do { \ + if (_nothing_conv_real_dict_after_start( \ + tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \ + &mpsv) != NOTDONE) { \ + goto typval_encode_stop_converting_one_item; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict) +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) + +static inline void _nothing_conv_dict_end(typval_T *const tv, + dict_T **const dictp, + const void *const nodictvar) + FUNC_ATTR_ALWAYS_INLINE +{ + if ((const void *)dictp != nodictvar) { + tv_dict_unref(*dictp); + *dictp = NULL; + } +} +#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ + _nothing_conv_dict_end(tv, (dict_T **)&dict, \ + (void *)&TYPVAL_ENCODE_NODICT_VAR) + +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) + +#define TYPVAL_ENCODE_SCOPE static +#define TYPVAL_ENCODE_NAME nothing +#define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const +#define TYPVAL_ENCODE_FIRST_ARG_NAME ignored +#include "nvim/eval/typval_encode.c.h" +#undef TYPVAL_ENCODE_SCOPE +#undef TYPVAL_ENCODE_NAME +#undef TYPVAL_ENCODE_FIRST_ARG_TYPE +#undef TYPVAL_ENCODE_FIRST_ARG_NAME + +#undef TYPVAL_ENCODE_ALLOW_SPECIALS +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_FUNC_START +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF +#undef TYPVAL_ENCODE_CONV_FUNC_END +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START +#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_RECURSE + +/// Free memory for a variable value and set the value to NULL or 0 +/// +/// @param[in,out] tv Value to free. +void tv_clear(typval_T *const tv) +{ + if (tv != NULL && tv->v_type != VAR_UNKNOWN) { + const int evn_ret = encode_vim_to_nothing(NULL, tv, + _("tv_clear() argument")); + (void)evn_ret; + assert(evn_ret == OK); + } +} + +//{{{3 Free + +/// Free allocated VimL object and value stored inside +/// +/// @param tv Object to free. +void tv_free(typval_T *tv) +{ + if (tv != NULL) { + switch (tv->v_type) { + case VAR_PARTIAL: { + partial_unref(tv->vval.v_partial); + break; + } + case VAR_FUNC: { + func_unref(tv->vval.v_string); + // FALLTHROUGH + } + case VAR_STRING: { + xfree(tv->vval.v_string); + break; + } + case VAR_LIST: { + tv_list_unref(tv->vval.v_list); + break; + } + case VAR_DICT: { + tv_dict_unref(tv->vval.v_dict); + break; + } + case VAR_SPECIAL: + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_UNKNOWN: { + break; + } + } + xfree(tv); + } +} + +//{{{3 Copy + +/// Copy typval from one location to another +/// +/// When needed allocates string or increases reference count. Does not make +/// a copy of a container, but copies its reference! +/// +/// It is OK for `from` and `to` to point to the same location; this is used to +/// make a copy later. +/// +/// @param[in] from Location to copy from. +/// @param[out] to Location to copy to. +void tv_copy(typval_T *const from, typval_T *const to) +{ + to->v_type = from->v_type; + to->v_lock = VAR_UNLOCKED; + memmove(&to->vval, &from->vval, sizeof(to->vval)); + switch (from->v_type) { + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_SPECIAL: { + break; + } + case VAR_STRING: + case VAR_FUNC: { + if (from->vval.v_string != NULL) { + to->vval.v_string = vim_strsave(from->vval.v_string); + if (from->v_type == VAR_FUNC) { + func_ref(to->vval.v_string); + } + } + break; + } + case VAR_PARTIAL: { + if (to->vval.v_partial != NULL) { + to->vval.v_partial->pt_refcount++; + } + break; + } + case VAR_LIST: { + if (from->vval.v_list != NULL) { + to->vval.v_list->lv_refcount++; + } + break; + } + case VAR_DICT: { + if (from->vval.v_dict != NULL) { + to->vval.v_dict->dv_refcount++; + } + break; + } + case VAR_UNKNOWN: { + emsgf(_(e_intern2), "tv_copy(UNKNOWN)"); + break; + } + } +} + +//{{{2 Locks + +/// Lock or unlock an item +/// +/// @param[out] tv Item to (un)lock. +/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. +/// @param[in] lock True if it is needed to lock an item, false to unlock. +void tv_item_lock(typval_T *const tv, const int deep, const bool lock) + FUNC_ATTR_NONNULL_ALL +{ + // TODO(ZyX-I): Make this not recursive + static int recurse = 0; + + if (recurse >= DICT_MAXNEST) { + emsgf(_("E743: variable nested too deep for (un)lock")); + return; + } + if (deep == 0) { + return; + } + recurse++; + + // lock/unlock the item itself +#define CHANGE_LOCK(lock, var) \ + do { \ + var = ((VarLockStatus[]) { \ + [VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ + [VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ + [VAR_FIXED] = VAR_FIXED, \ + })[var]; \ + } while (0) + CHANGE_LOCK(lock, tv->v_lock); + + switch (tv->v_type) { + case VAR_LIST: { + list_T *const l = tv->vval.v_list; + if (l != NULL) { + CHANGE_LOCK(lock, l->lv_lock); + if (deep < 0 || deep > 1) { + // Recursive: lock/unlock the items the List contains. + for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { + tv_item_lock(&li->li_tv, deep - 1, lock); + } + } + } + break; + } + case VAR_DICT: { + dict_T *const d = tv->vval.v_dict; + if (d != NULL) { + CHANGE_LOCK(lock, d->dv_lock); + if (deep < 0 || deep > 1) { + // recursive: lock/unlock the items the List contains + TV_DICT_ITER(d, di, { + tv_item_lock(&di->di_tv, deep - 1, lock); + }); + } + } + break; + } + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_STRING: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_SPECIAL: { + break; + } + case VAR_UNKNOWN: { + assert(false); + } + } +#undef CHANGE_LOCK + recurse--; +} + +/// Check whether VimL value is locked itself or refers to a locked container +/// +/// @param[in] tv Value to check. +/// +/// @return True if value is locked, false otherwise. +bool tv_islocked(const typval_T *const tv) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + return ((tv->v_lock == VAR_LOCKED) + || (tv->v_type == VAR_LIST + && tv->vval.v_list != NULL + && (tv->vval.v_list->lv_lock == VAR_LOCKED)) + || (tv->v_type == VAR_DICT + && tv->vval.v_dict != NULL + && (tv->vval.v_dict->dv_lock == VAR_LOCKED))); +} + +/// Return true if typval is locked +/// +/// Also gives an error message when typval is locked. +/// +/// @param[in] lock Lock status. +/// @param[in] name Variable name, used in the error message. +/// @param[in] name_len Variable name length. +/// +/// @return true if variable is locked, false otherwise. +bool tv_check_lock(const VarLockStatus lock, const char *const name, + const size_t name_len) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *error_message = NULL; + switch (lock) { + case VAR_UNLOCKED: { + return false; + } + case VAR_LOCKED: { + error_message = N_("E741: Value is locked: %.*s"); + break; + } + case VAR_FIXED: { + error_message = N_("E742: Cannot change value of %.*s"); + break; + } + } + assert(error_message != NULL); + + const char *const unknown_name = _("Unknown"); + + emsgf(_(error_message), (name != NULL ? name_len : strlen(unknown_name)), + (name != NULL ? name : unknown_name)); + + return true; +} + +//{{{2 Comparison + +static int tv_equal_recurse_limit; + +/// Compare two VimL values +/// +/// Like "==", but strings and numbers are different, as well as floats and +/// numbers. +/// +/// @warning Too nested structures may be considered equal even if they are not. +/// +/// @param[in] tv1 First value to compare. +/// @param[in] tv2 Second value to compare. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +/// +/// @return true if values are equal. +bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, + const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + // TODO(ZyX-I): Make this not recursive + static int recursive_cnt = 0; // Catch recursive loops. + + if (!(tv_is_func(*tv1) && tv_is_func(*tv2)) && tv1->v_type != tv2->v_type) { + return false; + } + + // Catch lists and dicts that have an endless loop by limiting + // recursiveness to a limit. We guess they are equal then. + // A fixed limit has the problem of still taking an awful long time. + // Reduce the limit every time running into it. That should work fine for + // deeply linked structures that are not recursively linked and catch + // recursiveness quickly. + if (!recursive) { + tv_equal_recurse_limit = 1000; + } + if (recursive_cnt >= tv_equal_recurse_limit) { + tv_equal_recurse_limit--; + return true; + } + + switch (tv1->v_type) { + case VAR_LIST: { + recursive_cnt++; + const bool r = tv_list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, + true); + recursive_cnt--; + return r; + } + case VAR_DICT: { + recursive_cnt++; + const bool r = tv_dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, + true); + recursive_cnt--; + return r; + } + case VAR_PARTIAL: + case VAR_FUNC: { + if ((tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial == NULL) + || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial == NULL)) { + return false; + } + recursive_cnt++; + const bool r = func_equal(tv1, tv2, ic); + recursive_cnt--; + return r; + } + case VAR_NUMBER: { + return tv1->vval.v_number == tv2->vval.v_number; + } + case VAR_FLOAT: { + return tv1->vval.v_float == tv2->vval.v_float; + } + case VAR_STRING: { + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *s1 = tv_get_string_buf(tv1, buf1); + const char *s2 = tv_get_string_buf(tv2, buf2); + return mb_strcmp_ic((bool)ic, s1, s2) == 0; + } + case VAR_SPECIAL: { + return tv1->vval.v_special == tv2->vval.v_special; + } + case VAR_UNKNOWN: { + // VAR_UNKNOWN can be the result of an invalid expression, let’s say it + // does not equal anything, not even self. + return false; + } + } + + assert(false); + return false; +} + +//{{{2 Type checks + +/// Check that given value is a number or string +/// +/// Error messages are compatible with tv_get_number() previously used for the +/// same purpose in buf*() functions. Special values are not accepted (previous +/// behaviour: silently fail to find buffer). +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_str_or_nr(const typval_T *const tv) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_STRING: { + return true; + } + case VAR_FLOAT: { + emsgf(_("E805: Expected a Number or a String, Float found")); + return false; + } + case VAR_PARTIAL: + case VAR_FUNC: { + emsgf(_("E703: Expected a Number or a String, Funcref found")); + return false; + } + case VAR_LIST: { + emsgf(_("E745: Expected a Number or a String, List found")); + return false; + } + case VAR_DICT: { + emsgf(_("E728: Expected a Number or a String, Dictionary found")); + return false; + } + case VAR_SPECIAL: { + emsgf(_("E5300: Expected a Number or a String")); + return false; + } + case VAR_UNKNOWN: { + emsgf(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); + return false; + } + } + assert(false); + return false; +} + +#define FUNC_ERROR "E703: Using a Funcref as a Number" + +static const char *const num_errors[] = { + [VAR_PARTIAL]=N_(FUNC_ERROR), + [VAR_FUNC]=N_(FUNC_ERROR), + [VAR_LIST]=N_("E745: Using a List as a Number"), + [VAR_DICT]=N_("E728: Using a Dictionary as a Number"), + [VAR_FLOAT]=N_("E805: Using a Float as a Number"), + [VAR_UNKNOWN]=N_("E685: using an invalid value as a Number"), +}; + +#undef FUNC_ERROR + +/// Check that given value is a number or can be converted to it +/// +/// Error messages are compatible with tv_get_number_chk() previously used for +/// the same purpose. +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_num(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_SPECIAL: + case VAR_STRING: { + return true; + } + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + emsgf(_(num_errors[tv->v_type])); + return false; + } + } + assert(false); + return false; +} + +#define FUNC_ERROR "E729: using Funcref as a String" + +static const char *const str_errors[] = { + [VAR_PARTIAL]=N_(FUNC_ERROR), + [VAR_FUNC]=N_(FUNC_ERROR), + [VAR_LIST]=N_("E730: using List as a String"), + [VAR_DICT]=N_("E731: using Dictionary as a String"), + [VAR_FLOAT]=((const char *)e_float_as_string), + [VAR_UNKNOWN]=N_("E908: using an invalid value as a String"), +}; + +#undef FUNC_ERROR + +/// Check that given value is a string or can be converted to it +/// +/// Error messages are compatible with tv_get_string_chk() previously used for +/// the same purpose. +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_str(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_SPECIAL: + case VAR_STRING: { + return true; + } + case VAR_PARTIAL: + case VAR_FUNC: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + emsgf(_(str_errors[tv->v_type])); + return false; + } + } + assert(false); + return false; +} + +//{{{2 Get + +/// Get the number value of a VimL object +/// +/// @note Use tv_get_number_chk() if you need to determine whether there was an +/// error. +/// +/// @param[in] tv Object to get value from. +/// +/// @return Number value: vim_str2nr() output for VAR_STRING objects, value +/// for VAR_NUMBER objects, -1 for other types. +varnumber_T tv_get_number(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool error = false; + return tv_get_number_chk(tv, &error); +} + +/// Get the number value of a VimL object +/// +/// @param[in] tv Object to get value from. +/// @param[out] ret_error If type error occurred then `true` will be written +/// to this location. Otherwise it is not touched. +/// +/// @note Needs to be initialized to `false` to be +/// useful. +/// +/// @return Number value: vim_str2nr() output for VAR_STRING objects, value +/// for VAR_NUMBER objects, -1 (ret_error == NULL) or 0 (otherwise) for +/// other types. +varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) +{ + switch (tv->v_type) { + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: { + emsgf(_(num_errors[tv->v_type])); + break; + } + case VAR_NUMBER: { + return tv->vval.v_number; + } + case VAR_STRING: { + varnumber_T n = 0; + if (tv->vval.v_string != NULL) { + long nr; + vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &nr, NULL, 0); + n = (varnumber_T)nr; + } + return n; + } + case VAR_SPECIAL: { + switch (tv->vval.v_special) { + case kSpecialVarTrue: { + return 1; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + return 0; + } + } + break; + } + case VAR_UNKNOWN: { + emsgf(_(e_intern2), "tv_get_number(UNKNOWN)"); + break; + } + } + if (ret_error != NULL) { + *ret_error = true; + } + return (ret_error == NULL ? -1 : 0); +} + +/// Get the line number from VimL object +/// +/// @param[in] tv Object to get value from. Is expected to be a number or +/// a special string like ".", "$", … (works with current buffer +/// only). +/// +/// @return Line number or -1 or 0. +linenr_T tv_get_lnum(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + linenr_T lnum = tv_get_number_chk(tv, NULL); + if (lnum == 0) { // No valid number, try using same function as line() does. + int fnum; + pos_T *const fp = var2fpos(tv, true, &fnum); + if (fp != NULL) { + lnum = fp->lnum; + } + } + return lnum; +} + +/// Get the floating-point value of a VimL object +/// +/// Raises an error if object is not number or floating-point. +/// +/// @param[in] tv Object to get value of. +/// +/// @return Floating-point value of the variable or zero. +float_T tv_get_float(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: { + return (float_T)(tv->vval.v_number); + } + case VAR_FLOAT: { + return tv->vval.v_float; + } + case VAR_PARTIAL: + case VAR_FUNC: { + emsgf(_("E891: Using a Funcref as a Float")); + break; + } + case VAR_STRING: { + emsgf(_("E892: Using a String as a Float")); + break; + } + case VAR_LIST: { + emsgf(_("E893: Using a List as a Float")); + break; + } + case VAR_DICT: { + emsgf(_("E894: Using a Dictionary as a Float")); + break; + } + case VAR_SPECIAL: { + emsgf(_("E907: Using a special value as a Float")); + break; + } + case VAR_UNKNOWN: { + emsgf(_(e_intern2), "tv_get_float(UNKNOWN)"); + break; + } + } + return 0; +} + +/// Get the string value of a VimL object +/// +/// @param[in] tv Object to get value of. +/// @param buf Buffer used to hold numbers and special variables converted to +/// string. When function encounters one of these stringified value +/// will be written to buf and buf will be returned. +/// +/// Buffer must have NUMBUFLEN size. +/// +/// @return Object value if it is VAR_STRING object, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or NULL. +const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: { + snprintf(buf, NUMBUFLEN, "%" PRIdVARNUMBER, tv->vval.v_number); + return buf; + } + case VAR_STRING: { + if (tv->vval.v_string != NULL) { + return (const char *)tv->vval.v_string; + } + return ""; + } + case VAR_SPECIAL: { + STRCPY(buf, encode_special_var_names[tv->vval.v_special]); + return buf; + } + case VAR_PARTIAL: + case VAR_FUNC: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + emsgf(_(str_errors[tv->v_type])); + return false; + } + } + return NULL; +} + +/// Get the string value of a VimL object +/// +/// @warning For number and special values it uses a single, static buffer. It +/// may be used only once, next call to get_tv_string may reuse it. Use +/// tv_get_string_buf() if you need to use tv_get_string() output after +/// calling it again. +/// +/// @param[in] tv Object to get value of. +/// +/// @return Object value if it is VAR_STRING object, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or NULL. +const char *tv_get_string_chk(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char mybuf[NUMBUFLEN]; + + return tv_get_string_buf_chk(tv, mybuf); +} + +/// Get the string value of a VimL object +/// +/// @warning For number and special values it uses a single, static buffer. It +/// may be used only once, next call to get_tv_string may reuse it. Use +/// tv_get_string_buf() if you need to use tv_get_string() output after +/// calling it again. +/// +/// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but +/// return NULL on error. +/// +/// @param[in] tv Object to get value of. +/// +/// @return Object value if it is VAR_STRING object, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty +/// string. +const char *tv_get_string(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char mybuf[NUMBUFLEN]; + return tv_get_string_buf((typval_T *)tv, mybuf); +} + +/// Get the string value of a VimL object +/// +/// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but +/// return NULL on error. +/// +/// @param[in] tv Object to get value of. +/// @param buf Buffer used to hold numbers and special variables converted to +/// string. When function encounters one of these stringified value +/// will be written to buf and buf will be returned. +/// +/// Buffer must have NUMBUFLEN size. +/// +/// @return Object value if it is VAR_STRING object, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty +/// string. +const char *tv_get_string_buf(const typval_T *const tv, char *const buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *const res = (const char *)tv_get_string_buf_chk(tv, buf); + + return res != NULL ? res : ""; +} diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h new file mode 100644 index 0000000000..7eab22bc12 --- /dev/null +++ b/src/nvim/eval/typval.h @@ -0,0 +1,429 @@ +#ifndef NVIM_EVAL_TYPVAL_H +#define NVIM_EVAL_TYPVAL_H + +#include <limits.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <stdbool.h> + +#include "nvim/types.h" +#include "nvim/hashtab.h" +#include "nvim/garray.h" +#include "nvim/mbyte.h" +#include "nvim/func_attr.h" +#include "nvim/lib/queue.h" +#include "nvim/profile.h" // for proftime_T +#include "nvim/pos.h" // for linenr_T +#include "nvim/gettext.h" +#include "nvim/message.h" + +/// Type used for VimL VAR_NUMBER values +typedef int varnumber_T; + +/// Type used for VimL VAR_FLOAT values +typedef double float_T; + +/// Maximal possible value of varnumber_T variable +#define VARNUMBER_MAX INT_MAX + +/// Mimimal possible value of varnumber_T variable +#define VARNUMBER_MIN INT_MIN +#define PRIdVARNUMBER "d" + +/// %d printf format specifier for varnumber_T +#define PRIdVARNUMBER "d" + +typedef struct listvar_S list_T; +typedef struct dictvar_S dict_T; +typedef struct partial_S partial_T; + +typedef struct ufunc ufunc_T; + +typedef enum { + kCallbackNone, + kCallbackFuncref, + kCallbackPartial, +} CallbackType; + +typedef struct { + union { + char_u *funcref; + partial_T *partial; + } data; + CallbackType type; +} Callback; +#define CALLBACK_NONE ((Callback){ .type = kCallbackNone }) + +/// Structure holding dictionary watcher +typedef struct dict_watcher { + Callback callback; + char *key_pattern; + size_t key_pattern_len; + QUEUE node; + bool busy; // prevent recursion if the dict is changed in the callback +} DictWatcher; + +/// Special variable values +typedef enum { + kSpecialVarFalse, ///< v:false + kSpecialVarTrue, ///< v:true + kSpecialVarNull, ///< v:null +} SpecialVarValue; + +/// Variable lock status for typval_T.v_lock +typedef enum { + VAR_UNLOCKED = 0, ///< Not locked. + VAR_LOCKED = 1, ///< User lock, can be unlocked. + VAR_FIXED = 2, ///< Locked forever. +} VarLockStatus; + +/// VimL variable types, for use in typval_T.v_type +typedef enum { + VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. + VAR_NUMBER, ///< Number, .v_number is used. + VAR_STRING, ///< String, .v_string is used. + VAR_FUNC, ///< Function reference, .v_string is used as function name. + VAR_LIST, ///< List, .v_list is used. + VAR_DICT, ///< Dictionary, .v_dict is used. + VAR_FLOAT, ///< Floating-point value, .v_float is used. + VAR_SPECIAL, ///< Special value (true, false, null), .v_special + ///< is used. + VAR_PARTIAL, ///< Partial, .v_partial is used. +} VarType; + +/// Structure that holds an internal variable value +typedef struct { + VarType v_type; ///< Variable type. + VarLockStatus v_lock; ///< Variable lock status. + union typval_vval_union { + varnumber_T v_number; ///< Number, for VAR_NUMBER. + SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. + float_T v_float; ///< Floating-point number, for VAR_FLOAT. + char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. + list_T *v_list; ///< List for VAR_LIST, can be NULL. + dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. + partial_T *v_partial; ///< Closure: function with args. + } vval; ///< Actual value. +} typval_T; + +/// Values for (struct dictvar_S).dv_scope +typedef enum { + VAR_NO_SCOPE = 0, ///< Not a scope dictionary. + VAR_SCOPE = 1, ///< Scope dictionary which requires prefix (a:, v:, …). + VAR_DEF_SCOPE = 2, ///< Scope dictionary which may be accessed without prefix + ///< (l:, g:). +} ScopeType; + +/// Structure to hold an item of a list +typedef struct listitem_S listitem_T; + +struct listitem_S { + listitem_T *li_next; ///< Next item in list. + listitem_T *li_prev; ///< Previous item in list. + typval_T li_tv; ///< Item value. +}; + +/// Structure used by those that are using an item in a list +typedef struct listwatch_S listwatch_T; + +struct listwatch_S { + listitem_T *lw_item; ///< Item being watched. + listwatch_T *lw_next; ///< Next watcher. +}; + +/// Structure to hold info about a list +struct listvar_S { + listitem_T *lv_first; ///< First item, NULL if none. + listitem_T *lv_last; ///< Last item, NULL if none. + int lv_refcount; ///< Reference count. + int lv_len; ///< Number of items. + listwatch_T *lv_watch; ///< First watcher, NULL if none. + int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx]. + listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx". + int lv_copyID; ///< ID used by deepcopy(). + list_T *lv_copylist; ///< Copied list used by deepcopy(). + VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED. + list_T *lv_used_next; ///< next list in used lists list. + list_T *lv_used_prev; ///< Previous list in used lists list. +}; + +// Static list with 10 items. Use init_static_list() to initialize. +typedef struct { + list_T sl_list; // must be first + listitem_T sl_items[10]; +} staticList10_T; + +// Structure to hold an item of a Dictionary. +// Also used for a variable. +// The key is copied into "di_key" to avoid an extra alloc/free for it. +struct dictitem_S { + typval_T di_tv; ///< type and value of the variable + char_u di_flags; ///< flags (only used for variable) + char_u di_key[1]; ///< key (actually longer!) +}; + +#define TV_DICTITEM_STRUCT(KEY_LEN) \ + struct { \ + typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ + uint8_t di_flags; /* Flags. */ \ + char_u di_key[KEY_LEN]; /* Key value. */ \ + } + +/// Structure to hold a scope dictionary +/// +/// @warning Must be compatible with dictitem_T. +/// +/// For use in find_var_in_ht to pretend that it found dictionary item when it +/// finds scope dictionary. +typedef TV_DICTITEM_STRUCT(1) ScopeDictDictItem; + +/// Structure to hold an item of a Dictionary +/// +/// @warning Must be compatible with ScopeDictDictItem. +/// +/// Also used for a variable. +typedef TV_DICTITEM_STRUCT() dictitem_T; + +/// Flags for dictitem_T.di_flags +typedef enum { + DI_FLAGS_RO = 1, ///< Read-only value + DI_FLAGS_RO_SBX = 2, ///< Value, read-only in the sandbox + DI_FLAGS_FIX = 4, ///< Fixed value: cannot be :unlet or remove()d. + DI_FLAGS_LOCK = 8, ///< Locked value. + DI_FLAGS_ALLOC = 16, ///< Separately allocated. +} DictItemFlags; + +/// Structure representing a Dictionary +struct dictvar_S { + VarLockStatus dv_lock; ///< Whole dictionary lock status. + ScopeType dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if + ///< dictionary represents a scope (i.e. g:, l: …). + int dv_refcount; ///< Reference count. + int dv_copyID; ///< ID used when recursivery traversing a value. + hashtab_T dv_hashtab; ///< Hashtab containing all items. + dict_T *dv_copydict; ///< Copied dict used by deepcopy(). + dict_T *dv_used_next; ///< Next dictionary in used dictionaries list. + dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list. + QUEUE watchers; ///< Dictionary key watchers set by user code. +}; + +/// Type used for script ID +typedef int scid_T; +/// Format argument for scid_T +#define PRIdSCID "d" + +// Structure to hold info for a function that is currently being executed. +typedef struct funccall_S funccall_T; + +/// Structure to hold info for a user function. +struct ufunc { + int uf_varargs; ///< variable nr of arguments + int uf_flags; + int uf_calls; ///< nr of active calls + bool uf_cleared; ///< func_clear() was already called + garray_T uf_args; ///< arguments + garray_T uf_lines; ///< function lines + int uf_profiling; ///< true when func is being profiled + // Profiling the function as a whole. + int uf_tm_count; ///< nr of calls + proftime_T uf_tm_total; ///< time spent in function + children + proftime_T uf_tm_self; ///< time spent in function itself + proftime_T uf_tm_children; ///< time spent in children this call + // Profiling the function per line. + int *uf_tml_count; ///< nr of times line was executed + proftime_T *uf_tml_total; ///< time spent in a line + children + proftime_T *uf_tml_self; ///< time spent in a line itself + proftime_T uf_tml_start; ///< start time for current line + proftime_T uf_tml_children; ///< time spent in children for this line + proftime_T uf_tml_wait; ///< start wait time for current line + int uf_tml_idx; ///< index of line being timed; -1 if none + int uf_tml_execed; ///< line being timed was executed + scid_T uf_script_ID; ///< ID of script where function was defined, + ///< used for s: variables + int uf_refcount; ///< reference count, see func_name_refcount() + funccall_T *uf_scoped; ///< l: local variables for closure + char_u uf_name[1]; ///< name of function (actually longer); can + ///< start with <SNR>123_ (<SNR> is K_SPECIAL + ///< KS_EXTRA KE_SNR) +}; + +/// Maximum number of function arguments +#define MAX_FUNC_ARGS 20 + +struct partial_S { + int pt_refcount; ///< Reference count. + char_u *pt_name; ///< Function name; when NULL use pt_func->name. + ufunc_T *pt_func; ///< Function pointer; when NULL lookup function with + ///< pt_name. + bool pt_auto; ///< When true the partial was created by using dict.member + ///< in handle_subscript(). + int pt_argc; ///< Number of arguments. + typval_T *pt_argv; ///< Arguments in allocated array. + dict_T *pt_dict; ///< Dict for "self". +}; + +/// Structure used for explicit stack while garbage collecting hash tables +typedef struct ht_stack_S { + hashtab_T *ht; + struct ht_stack_S *prev; +} ht_stack_T; + +/// Structure used for explicit stack while garbage collecting lists +typedef struct list_stack_S { + list_T *list; + struct list_stack_S *prev; +} list_stack_T; + +// In a hashtab item "hi_key" points to "di_key" in a dictitem. +// This avoids adding a pointer to the hashtab item. + +/// Convert a hashitem pointer to a dictitem pointer +#define TV_DICT_HI2DI(hi) \ + ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) + +static inline long tv_list_len(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get the number of items in a list +/// +/// @param[in] l List to check. +static inline long tv_list_len(const list_T *const l) +{ + if (l == NULL) { + return 0; + } + return l->lv_len; +} + +static inline long tv_dict_len(const dict_T *const d) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get the number of items in a Dictionary +/// +/// @param[in] d Dictionary to check. +static inline long tv_dict_len(const dict_T *const d) +{ + if (d == NULL) { + return 0L; + } + return (long)d->dv_hashtab.ht_used; +} + +static inline bool tv_dict_is_watched(const dict_T *const d) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Check if dictionary is watched +/// +/// @param[in] d Dictionary to check. +/// +/// @return true if there is at least one watcher. +static inline bool tv_dict_is_watched(const dict_T *const d) +{ + return d && !QUEUE_EMPTY(&d->watchers); +} + +/// Initialize VimL object +/// +/// Initializes to unlocked VAR_UNKNOWN object. +/// +/// @param[out] tv Object to initialize. +static inline void tv_init(typval_T *const tv) +{ + if (tv != NULL) { + memset(tv, 0, sizeof(*tv)); + } +} + +#define TV_INITIAL_VALUE \ + ((typval_T) { \ + .v_type = VAR_UNKNOWN, \ + .v_lock = VAR_UNLOCKED, \ + }) + +/// Empty string +/// +/// Needed for hack which allows not allocating empty string and still not +/// crashing when freeing it. +extern const char *const tv_empty_string; + +/// Specifies that free_unref_items() function has (not) been entered +extern bool tv_in_free_unref_items; + +/// Iterate over a dictionary +/// +/// @param[in] d Dictionary to iterate over. +/// @param di Name of the variable with current dictitem_T entry. +/// @param code Cycle body. +#define TV_DICT_ITER(d, di, code) \ + HASHTAB_ITER(&(d)->dv_hashtab, di##hi_, { \ + { \ + dictitem_T *const di = TV_DICT_HI2DI(di##hi_); \ + { \ + code \ + } \ + } \ + }) + +static inline bool tv_get_float_chk(const typval_T *const tv, + float_T *const ret_f) + REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; + +// FIXME circular dependency, cannot import message.h. +bool emsgf(const char *const fmt, ...); + +/// Get the float value +/// +/// Raises an error if object is not number or floating-point. +/// +/// @param[in] tv VimL object to get value from. +/// @param[out] ret_f Location where resulting float is stored. +/// +/// @return true in case of success, false if tv is not a number or float. +static inline bool tv_get_float_chk(const typval_T *const tv, + float_T *const ret_f) +{ + if (tv->v_type == VAR_FLOAT) { + *ret_f = tv->vval.v_float; + return true; + } + if (tv->v_type == VAR_NUMBER) { + *ret_f = (float_T)tv->vval.v_number; + return true; + } + emsgf(_("E808: Number or Float required")); + return false; +} + +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) + REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE + REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE; + +/// Compute the `DictWatcher` address from a QUEUE node. +/// +/// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer +/// arithmetic). +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) +{ + return QUEUE_DATA(q, DictWatcher, node); +} + +static inline bool tv_is_func(const typval_T tv) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST; + +/// Check whether given typval_T contains a function +/// +/// That is, whether it contains VAR_FUNC or VAR_PARTIAL. +/// +/// @param[in] tv Typval to check. +/// +/// @return True if it is a function or a partial, false otherwise. +static inline bool tv_is_func(const typval_T tv) +{ + return tv.v_type == VAR_FUNC || tv.v_type == VAR_PARTIAL; +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/typval.h.generated.h" +#endif +#endif // NVIM_EVAL_TYPVAL_H diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 4ff5589887..ad54eef4a0 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -242,7 +242,7 @@ #include <assert.h> #include "nvim/lib/kvec.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/eval/encode.h" #include "nvim/func_attr.h" #include "nvim/eval/typval_encode.h" @@ -406,11 +406,11 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( const dictitem_T *val_di; if (TYPVAL_ENCODE_ALLOW_SPECIALS && tv->vval.v_dict->dv_hashtab.ht_used == 2 - && (type_di = dict_find((dict_T *)tv->vval.v_dict, - (char_u *)"_TYPE", -1)) != NULL + && (type_di = tv_dict_find((dict_T *)tv->vval.v_dict, + S_LEN("_TYPE"))) != NULL && type_di->di_tv.v_type == VAR_LIST - && (val_di = dict_find((dict_T *)tv->vval.v_dict, - (char_u *)"_VAL", -1)) != NULL) { + && (val_di = tv_dict_find((dict_T *)tv->vval.v_dict, + S_LEN("_VAL"))) != NULL) { size_t i; for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { @@ -662,7 +662,7 @@ typval_encode_stop_converting_one_item: while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { cur_mpsv->data.d.hi++; } - dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); + dictitem_T *const di = TV_DICT_HI2DI(cur_mpsv->data.d.hi); cur_mpsv->data.d.todo--; cur_mpsv->data.d.hi++; TYPVAL_ENCODE_CONV_STR_STRING(NULL, &di->di_key[0], diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h index 46145c5d03..3475f6d8b3 100644 --- a/src/nvim/eval/typval_encode.h +++ b/src/nvim/eval/typval_encode.h @@ -11,7 +11,7 @@ #include <assert.h> #include "nvim/lib/kvec.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/func_attr.h" /// Type of the stack entry diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h deleted file mode 100644 index 39028fdb11..0000000000 --- a/src/nvim/eval_defs.h +++ /dev/null @@ -1,285 +0,0 @@ -#ifndef NVIM_EVAL_DEFS_H -#define NVIM_EVAL_DEFS_H - -#include <limits.h> -#include <stddef.h> - -#include "nvim/hashtab.h" -#include "nvim/lib/queue.h" -#include "nvim/garray.h" // for garray_T -#include "nvim/profile.h" // for proftime_T -#include "nvim/pos.h" // for linenr_T - -typedef int varnumber_T; -typedef double float_T; - -#define VARNUMBER_MAX INT_MAX -#define VARNUMBER_MIN INT_MIN - -typedef struct listvar_S list_T; -typedef struct dictvar_S dict_T; -typedef struct partial_S partial_T; - -/// Special variable values -typedef enum { - kSpecialVarFalse, ///< v:false - kSpecialVarTrue, ///< v:true - kSpecialVarNull, ///< v:null -} SpecialVarValue; - -/// Variable lock status for typval_T.v_lock -typedef enum { - VAR_UNLOCKED = 0, ///< Not locked. - VAR_LOCKED = 1, ///< User lock, can be unlocked. - VAR_FIXED = 2, ///< Locked forever. -} VarLockStatus; - -/// VimL variable types, for use in typval_T.v_type -typedef enum { - VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. - VAR_NUMBER, ///< Number, .v_number is used. - VAR_STRING, ///< String, .v_string is used. - VAR_FUNC, ///< Function reference, .v_string is used as function name. - VAR_LIST, ///< List, .v_list is used. - VAR_DICT, ///< Dictionary, .v_dict is used. - VAR_FLOAT, ///< Floating-point value, .v_float is used. - VAR_SPECIAL, ///< Special value (true, false, null), .v_special - ///< is used. - VAR_PARTIAL, ///< Partial, .v_partial is used. -} VarType; - -/// Structure that holds an internal variable value -typedef struct { - VarType v_type; ///< Variable type. - VarLockStatus v_lock; ///< Variable lock status. - union typval_vval_union { - varnumber_T v_number; ///< Number, for VAR_NUMBER. - SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. - float_T v_float; ///< Floating-point number, for VAR_FLOAT. - char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. - list_T *v_list; ///< List for VAR_LIST, can be NULL. - dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. - partial_T *v_partial; ///< Closure: function with args. - } vval; ///< Actual value. -} typval_T; - -/* Values for "dv_scope". */ -#define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */ -#define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not - allowed to mask existing functions */ - -/* - * Structure to hold an item of a list: an internal variable without a name. - */ -typedef struct listitem_S listitem_T; - -struct listitem_S { - listitem_T *li_next; /* next item in list */ - listitem_T *li_prev; /* previous item in list */ - typval_T li_tv; /* type and value of the variable */ -}; - -/* - * Struct used by those that are using an item in a list. - */ -typedef struct listwatch_S listwatch_T; - -struct listwatch_S { - listitem_T *lw_item; /* item being watched */ - listwatch_T *lw_next; /* next watcher */ -}; - -/* - * Structure to hold info about a list. - */ -struct listvar_S { - listitem_T *lv_first; ///< First item, NULL if none. - listitem_T *lv_last; ///< Last item, NULL if none. - int lv_refcount; ///< Reference count. - int lv_len; ///< Number of items. - listwatch_T *lv_watch; ///< First watcher, NULL if none. - int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx]. - listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx". - int lv_copyID; ///< ID used by deepcopy(). - list_T *lv_copylist; ///< Copied list used by deepcopy(). - VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED. - list_T *lv_used_next; ///< next list in used lists list. - list_T *lv_used_prev; ///< Previous list in used lists list. -}; - -// Static list with 10 items. Use init_static_list() to initialize. -typedef struct { - list_T sl_list; // must be first - listitem_T sl_items[10]; -} staticList10_T; - -// Structure to hold an item of a Dictionary. -// Also used for a variable. -// The key is copied into "di_key" to avoid an extra alloc/free for it. -struct dictitem_S { - typval_T di_tv; ///< type and value of the variable - char_u di_flags; ///< flags (only used for variable) - char_u di_key[1]; ///< key (actually longer!) -}; - -typedef struct dictitem_S dictitem_T; - -/// A dictitem with a 16 character key (plus NUL) -struct dictitem16_S { - typval_T di_tv; ///< type and value of the variable - char_u di_flags; ///< flags (only used for variable) - char_u di_key[17]; ///< key -}; - -typedef struct dictitem16_S dictitem16_T; - - -#define DI_FLAGS_RO 1 // "di_flags" value: read-only variable -#define DI_FLAGS_RO_SBX 2 // "di_flags" value: read-only in the sandbox -#define DI_FLAGS_FIX 4 // "di_flags" value: fixed: no :unlet or remove() -#define DI_FLAGS_LOCK 8 // "di_flags" value: locked variable -#define DI_FLAGS_ALLOC 16 // "di_flags" value: separately allocated - -/// Structure representing a Dictionary -struct dictvar_S { - VarLockStatus dv_lock; ///< Whole dictionary lock status. - char dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if - ///< dictionary represents a scope (i.e. g:, l: …). - int dv_refcount; ///< Reference count. - int dv_copyID; ///< ID used when recursivery traversing a value. - hashtab_T dv_hashtab; ///< Hashtab containing all items. - dict_T *dv_copydict; ///< Copied dict used by deepcopy(). - dict_T *dv_used_next; ///< Next dictionary in used dictionaries list. - dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list. - QUEUE watchers; ///< Dictionary key watchers set by user code. -}; - -typedef int scid_T; // script ID -typedef struct funccall_S funccall_T; - -// Structure to hold info for a user function. -typedef struct ufunc ufunc_T; - -struct ufunc { - int uf_varargs; ///< variable nr of arguments - int uf_flags; - int uf_calls; ///< nr of active calls - bool uf_cleared; ///< func_clear() was already called - garray_T uf_args; ///< arguments - garray_T uf_lines; ///< function lines - int uf_profiling; ///< true when func is being profiled - // Profiling the function as a whole. - int uf_tm_count; ///< nr of calls - proftime_T uf_tm_total; ///< time spent in function + children - proftime_T uf_tm_self; ///< time spent in function itself - proftime_T uf_tm_children; ///< time spent in children this call - // Profiling the function per line. - int *uf_tml_count; ///< nr of times line was executed - proftime_T *uf_tml_total; ///< time spent in a line + children - proftime_T *uf_tml_self; ///< time spent in a line itself - proftime_T uf_tml_start; ///< start time for current line - proftime_T uf_tml_children; ///< time spent in children for this line - proftime_T uf_tml_wait; ///< start wait time for current line - int uf_tml_idx; ///< index of line being timed; -1 if none - int uf_tml_execed; ///< line being timed was executed - scid_T uf_script_ID; ///< ID of script where function was defined, - // used for s: variables - int uf_refcount; ///< reference count, see func_name_refcount() - funccall_T *uf_scoped; ///< l: local variables for closure - char_u uf_name[1]; ///< name of function (actually longer); can - // start with <SNR>123_ (<SNR> is K_SPECIAL - // KS_EXTRA KE_SNR) -}; - -/// Maximum number of function arguments -#define MAX_FUNC_ARGS 20 -#define VAR_SHORT_LEN 20 // short variable name length -#define FIXVAR_CNT 12 // number of fixed variables - -// structure to hold info for a function that is currently being executed. -struct funccall_S { - ufunc_T *func; ///< function being called - int linenr; ///< next line to be executed - int returned; ///< ":return" used - struct { ///< fixed variables for arguments - dictitem_T var; ///< variable (without room for name) - char_u room[VAR_SHORT_LEN]; ///< room for the name - } fixvar[FIXVAR_CNT]; - dict_T l_vars; ///< l: local function variables - dictitem_T l_vars_var; ///< variable for l: scope - dict_T l_avars; ///< a: argument variables - dictitem_T l_avars_var; ///< variable for a: scope - list_T l_varlist; ///< list for a:000 - listitem_T l_listitems[MAX_FUNC_ARGS]; ///< listitems for a:000 - typval_T *rettv; ///< return value - linenr_T breakpoint; ///< next line with breakpoint or zero - int dbg_tick; ///< debug_tick when breakpoint was set - int level; ///< top nesting level of executed function - proftime_T prof_child; ///< time spent in a child - funccall_T *caller; ///< calling function or NULL - int fc_refcount; ///< number of user functions that reference - // this funccal - int fc_copyID; ///< for garbage collection - garray_T fc_funcs; ///< list of ufunc_T* which keep a reference - // to "func" -}; - -// structure used by trans_function_name() -typedef struct { - dict_T *fd_dict; ///< Dictionary used. - char_u *fd_newkey; ///< New key in "dict" in allocated memory. - dictitem_T *fd_di; ///< Dictionary item used. -} funcdict_T; - -struct partial_S { - int pt_refcount; ///< Reference count. - char_u *pt_name; ///< Function name; when NULL use pt_func->name. - ufunc_T *pt_func; ///< Function pointer; when NULL lookup function - ///< with pt_name. - bool pt_auto; ///< when true the partial was created for using - ///< dict.member in handle_subscript(). - int pt_argc; ///< Number of arguments. - typval_T *pt_argv; ///< Arguments in allocated array. - dict_T *pt_dict; ///< Dict for "self". -}; - -// structure used for explicit stack while garbage collecting hash tables -typedef struct ht_stack_S { - hashtab_T *ht; - struct ht_stack_S *prev; -} ht_stack_T; - -// structure used for explicit stack while garbage collecting lists -typedef struct list_stack_S { - list_T *list; - struct list_stack_S *prev; -} list_stack_T; - -// In a hashtab item "hi_key" points to "di_key" in a dictitem. -// This avoids adding a pointer to the hashtab item. - -/// Convert a dictitem pointer to a hashitem key pointer -#define DI2HIKEY(di) ((di)->di_key) - -/// Convert a hashitem key pointer to a dictitem pointer -#define HIKEY2DI(p) ((dictitem_T *)(p - offsetof(dictitem_T, di_key))) - -/// Convert a hashitem value pointer to a dictitem pointer -#define HIVAL2DI(p) \ - ((dictitem_T *)(((char *)p) - offsetof(dictitem_T, di_tv))) - -/// Convert a hashitem pointer to a dictitem pointer -#define HI2DI(hi) HIKEY2DI((hi)->hi_key) - -/// Type of assert_* check being performed -typedef enum -{ - ASSERT_EQUAL, - ASSERT_NOTEQUAL, - ASSERT_MATCH, - ASSERT_NOTMATCH, - ASSERT_INRANGE, - ASSERT_OTHER, -} assert_type_T; - -#endif // NVIM_EVAL_DEFS_H diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 5cbf7f9ce7..26d70a5e6d 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -21,7 +21,7 @@ struct process { int pid, status, refcount; // set to the hrtime of when process_stop was called for the process. uint64_t stopped_time; - char *cwd; + const char *cwd; char **argv; Stream *in, *out, *err; process_exit_cb cb; diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 92efc9fa2e..2737dad305 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -89,7 +89,10 @@ static void on_rbuffer_nonfull(RBuffer *buf, void *data) static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) { Stream *stream = handle->data; - buf->base = rbuffer_write_ptr(stream->buffer, &buf->len); + // `uv_buf_t.len` happens to have different size on Windows. + size_t write_count; + buf->base = rbuffer_write_ptr(stream->buffer, &write_count); + buf->len = write_count; } // Callback invoked by libuv after it copies the data into the buffer provided @@ -136,7 +139,10 @@ static void fread_idle_cb(uv_idle_t *handle) uv_fs_t req; Stream *stream = handle->data; - stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &stream->uvbuf.len); + // `uv_buf_t.len` happens to have different size on Windows. + size_t write_count; + stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &write_count); + stream->uvbuf.len = write_count; // the offset argument to uv_fs_read is int64_t, could someone really try // to read more than 9 quintillion (9e18) bytes? diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 1b83677807..9a847a4c0a 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -756,14 +756,6 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) linenr_T num_lines; // Num lines moved linenr_T last_line; // Last line in file after adding new text - // Moving lines seems to corrupt the folds, delete folding info now - // and recreate it when finished. Don't do this for manual folding, it - // would delete all folds. - bool isFolded = hasAnyFolding(curwin) && !foldmethodIsManual(curwin); - if (isFolded) { - deleteFoldRecurse(&curwin->w_folds); - } - if (dest >= line1 && dest < line2) { EMSG(_("E134: Move lines into themselves")); return FAIL; @@ -801,21 +793,29 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) * their final destination at the new text position -- webb */ last_line = curbuf->b_ml.ml_line_count; - mark_adjust(line1, line2, last_line - line2, 0L); - changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines); + mark_adjust_nofold(line1, line2, last_line - line2, 0L); if (dest >= line2) { - mark_adjust(line2 + 1, dest, -num_lines, 0L); + mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L); + FOR_ALL_TAB_WINDOWS(tab, win) { + if (win->w_buffer == curbuf) { + foldMoveRange(&win->w_folds, line1, line2, dest); + } + } curbuf->b_op_start.lnum = dest - num_lines + 1; curbuf->b_op_end.lnum = dest; } else { - mark_adjust(dest + 1, line1 - 1, num_lines, 0L); + mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L); + FOR_ALL_TAB_WINDOWS(tab, win) { + if (win->w_buffer == curbuf) { + foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2); + } + } curbuf->b_op_start.lnum = dest + 1; curbuf->b_op_end.lnum = dest + num_lines; } curbuf->b_op_start.col = curbuf->b_op_end.col = 0; - mark_adjust(last_line - num_lines + 1, last_line, - -(last_line - dest - extra), 0L); - changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra); + mark_adjust_nofold(last_line - num_lines + 1, last_line, + -(last_line - dest - extra), 0L); /* * Now we delete the original text -- webb @@ -851,11 +851,6 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) changed_lines(dest + 1, 0, line1 + num_lines, 0L); } - // recreate folds - if (isFolded) { - foldUpdateAll(curwin); - } - return OK; } @@ -1013,8 +1008,8 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) AppendToRedobuffLit(cmd, -1); xfree(cmd); - AppendToRedobuff((char_u *)"\n"); - bangredo = FALSE; + AppendToRedobuff("\n"); + bangredo = false; } /* * Add quotes around the command, for shells that need them. @@ -2284,20 +2279,24 @@ int do_ecmd( } else { win_T *the_curwin = curwin; - // Set the w_closing flag to avoid that autocommands close the window. + // Set w_closing to avoid that autocommands close the window. + // Set b_locked for the same reason. the_curwin->w_closing = true; + buf->b_locked++; + if (curbuf == old_curbuf.br_buf) { buf_copy_options(buf, BCO_ENTER); } // Close the link to the current buffer. This will set - // curwin->w_buffer to NULL. + // oldwin->w_buffer to NULL. u_sync(false); close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, false); the_curwin->w_closing = false; + buf->b_locked--; // autocmds may abort script processing if (aborting() && curwin->w_buffer != NULL) { @@ -2445,11 +2444,6 @@ int do_ecmd( retval = OK; /* - * Reset cursor position, could be used by autocommands. - */ - check_cursor(); - - /* * Check if we are editing the w_arg_idx file in the argument list. */ check_arg_idx(curwin); @@ -2964,7 +2958,7 @@ void sub_set_replacement(SubReplacementString sub) { xfree(old_sub.sub); if (sub.additional_elements != old_sub.additional_elements) { - list_unref(old_sub.additional_elements); + tv_list_unref(old_sub.additional_elements); } old_sub = sub; } @@ -3583,11 +3577,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) ui_cursor_goto(msg_row, msg_col); RedrawingDisabled = temp; - ++no_mapping; /* don't map this key */ - ++allow_keys; /* allow special keys */ + no_mapping++; // don't map this key typed = plain_vgetc(); - --allow_keys; - --no_mapping; + no_mapping--; /* clear the question */ msg_didout = FALSE; /* don't scroll up */ @@ -4772,8 +4764,8 @@ void fix_help_buffer(void) char_u *p; char_u *rt; - /* set filetype to "help". */ - set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL); + // Set filetype to "help". + set_option_value("ft", 0L, "help", OPT_LOCAL); if (!syntax_present(curwin)) { for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index 243b11255e..65bbd8a99e 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -4,8 +4,8 @@ #include <stdbool.h> #include "nvim/os/time.h" -#include "nvim/eval_defs.h" #include "nvim/pos.h" +#include "nvim/eval/typval.h" // flags for do_ecmd() #define ECMD_HIDE 0x01 // don't free the current buffer diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 5f81306fc1..92f0669422 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -34,7 +34,8 @@ local ADDR_ARGUMENTS = 2 local ADDR_LOADED_BUFFERS = 3 local ADDR_BUFFERS = 4 local ADDR_TABS = 5 -local ADDR_QUICKFIX = 6 +local ADDR_TABS_RELATIVE = 6 +local ADDR_QUICKFIX = 7 local ADDR_OTHER = 99 -- The following table is described in ex_cmds_defs.h file. @@ -2650,12 +2651,12 @@ return { { command='tab', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_wrongmodifier', }, { command='tabclose', - flags=bit.bor(RANGE, NOTADR, COUNT, BANG, TRLBAR, CMDWIN), + flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), addr_type=ADDR_TABS, func='ex_tabclose', }, @@ -2680,7 +2681,7 @@ return { { command='tabfirst', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabnext', }, { @@ -2692,13 +2693,13 @@ return { { command='tablast', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabnext', }, { command='tabnext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type=ADDR_TABS, func='ex_tabnext', }, { @@ -2709,32 +2710,32 @@ return { }, { command='tabonly', - flags=bit.bor(BANG, RANGE, NOTADR, TRLBAR, CMDWIN), + flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), addr_type=ADDR_TABS, func='ex_tabonly', }, { command='tabprevious', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type=ADDR_TABS_RELATIVE, func='ex_tabnext', }, { command='tabNext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type=ADDR_TABS_RELATIVE, func='ex_tabnext', }, { command='tabrewind', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabnext', }, { command='tabs', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabs', }, { diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 36f50ae7a2..eeace789b2 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -957,23 +957,21 @@ char_u *get_profile_name(expand_T *xp, int idx) } /// Handle command line completion for :profile command. -void set_context_in_profile_cmd(expand_T *xp, char_u *arg) +void set_context_in_profile_cmd(expand_T *xp, const char *arg) { - char_u *end_subcmd; - // Default: expand subcommands. xp->xp_context = EXPAND_PROFILE; pexpand_what = PEXP_SUBCMD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; - end_subcmd = skiptowhite(arg); + char_u *const end_subcmd = skiptowhite((const char_u *)arg); if (*end_subcmd == NUL) { return; } - if (end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0) { + if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) { xp->xp_context = EXPAND_FILES; - xp->xp_pattern = skipwhite(end_subcmd); + xp->xp_pattern = skipwhite((const char_u *)end_subcmd); return; } @@ -2073,9 +2071,9 @@ void ex_listdo(exarg_T *eap) // Clear 'shm' to avoid that the file message overwrites // any output from the command. p_shm_save = vim_strsave(p_shm); - set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); + set_option_value("shm", 0L, "", 0); do_argfile(eap, i); - set_option_value((char_u *)"shm", 0L, p_shm_save, 0); + set_option_value("shm", 0L, (char *)p_shm_save, 0); xfree(p_shm_save); } if (curwin->w_arg_idx != i) { @@ -2138,9 +2136,9 @@ void ex_listdo(exarg_T *eap) // Go to the next buffer. Clear 'shm' to avoid that the file // message overwrites any output from the command. p_shm_save = vim_strsave(p_shm); - set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); + set_option_value("shm", 0L, "", 0); goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum); - set_option_value((char_u *)"shm", 0L, p_shm_save, 0); + set_option_value("shm", 0L, (char *)p_shm_save, 0); xfree(p_shm_save); // If autocommands took us elsewhere, quit here. @@ -2256,8 +2254,8 @@ void ex_compiler(exarg_T *eap) } do_cmdline_cmd("command -nargs=* CompilerSet setlocal <args>"); } - do_unlet((char_u *)"g:current_compiler", true); - do_unlet((char_u *)"b:current_compiler", true); + do_unlet(S_LEN("g:current_compiler"), true); + do_unlet(S_LEN("b:current_compiler"), true); snprintf((char *)buf, bufsize, "compiler/%s.vim", eap->arg); if (source_runtime(buf, DIP_ALL) == FAIL) { @@ -2280,7 +2278,7 @@ void ex_compiler(exarg_T *eap) old_cur_comp); xfree(old_cur_comp); } else { - do_unlet((char_u *)"g:current_compiler", true); + do_unlet(S_LEN("g:current_compiler"), true); } } } @@ -2361,12 +2359,25 @@ int do_in_path(char_u *path, char_u *name, int flags, while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) { // Copy the path from 'runtimepath' to buf[]. copy_option_part(&rtp, buf, MAXPATHL, ","); + size_t buflen = STRLEN(buf); + + // Skip after or non-after directories. + if (flags & (DIP_NOAFTER | DIP_AFTER)) { + bool is_after = buflen >= 5 + && STRCMP(buf + buflen - 5, "after") == 0; + + if ((is_after && (flags & DIP_NOAFTER)) + || (!is_after && (flags & DIP_AFTER))) { + continue; + } + } + if (name == NULL) { (*callback)(buf, (void *)&cookie); if (!did_one) { did_one = (cookie == NULL); } - } else if (STRLEN(buf) + STRLEN(name) + 2 < MAXPATHL) { + } else if (buflen + STRLEN(name) + 2 < MAXPATHL) { add_pathsep((char *)buf); tail = buf + STRLEN(buf); @@ -2483,16 +2494,16 @@ static int APP_BOTH; static void add_pack_plugin(char_u *fname, void *cookie) { char_u *p4, *p3, *p2, *p1, *p; - char_u *new_rtp; - char_u *ffname = (char_u *)fix_fname((char *)fname); + + char *const ffname = fix_fname((char *)fname); if (ffname == NULL) { return; } - if (cookie != &APP_LOAD && strstr((char *)p_rtp, (char *)ffname) == NULL) { + if (cookie != &APP_LOAD && strstr((char *)p_rtp, ffname) == NULL) { // directory is not yet in 'runtimepath', add it - p4 = p3 = p2 = p1 = get_past_head(ffname); + p4 = p3 = p2 = p1 = get_past_head((char_u *)ffname); for (p = p1; *p; mb_ptr_adv(p)) { if (vim_ispathsep_nocolon(*p)) { p4 = p3; p3 = p2; p2 = p1; p1 = p; @@ -2508,13 +2519,13 @@ static void add_pack_plugin(char_u *fname, void *cookie) *p4 = NUL; // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences - size_t fname_len = STRLEN(ffname); - char_u *insp = p_rtp; + size_t fname_len = strlen(ffname); + const char *insp = (const char *)p_rtp; for (;;) { - if (vim_fnamencmp(insp, ffname, fname_len) == 0) { + if (path_fnamencmp(insp, ffname, fname_len) == 0) { break; } - insp = vim_strchr(insp, ','); + insp = strchr(insp, ','); if (insp == NULL) { break; } @@ -2523,10 +2534,10 @@ static void add_pack_plugin(char_u *fname, void *cookie) if (insp == NULL) { // not found, append at the end - insp = p_rtp + STRLEN(p_rtp); + insp = (const char *)p_rtp + STRLEN(p_rtp); } else { // append after the matching directory. - insp += STRLEN(ffname); + insp += strlen(ffname); while (*insp != NUL && *insp != ',') { insp++; } @@ -2534,31 +2545,40 @@ static void add_pack_plugin(char_u *fname, void *cookie) *p4 = c; // check if rtp/pack/name/start/name/after exists - char *afterdir = concat_fnames((char *)ffname, "after", true); + char *afterdir = concat_fnames(ffname, "after", true); size_t afterlen = 0; if (os_isdir((char_u *)afterdir)) { - afterlen = STRLEN(afterdir) + 1; // add one for comma + afterlen = strlen(afterdir) + 1; // add one for comma } - size_t oldlen = STRLEN(p_rtp); - size_t addlen = STRLEN(ffname) + 1; // add one for comma - new_rtp = try_malloc(oldlen + addlen + afterlen + 1); // add one for NUL + const size_t oldlen = STRLEN(p_rtp); + const size_t addlen = strlen(ffname) + 1; // add one for comma + const size_t new_rtp_len = oldlen + addlen + afterlen + 1; + // add one for NUL -------------------------------------^ + char *const new_rtp = try_malloc(new_rtp_len); if (new_rtp == NULL) { goto theend; } - uintptr_t keep = (uintptr_t)(insp - p_rtp); + const size_t keep = (size_t)(insp - (const char *)p_rtp); + size_t new_rtp_fill = 0; memmove(new_rtp, p_rtp, keep); - new_rtp[keep] = ','; - memmove(new_rtp + keep + 1, ffname, addlen); + new_rtp_fill += keep; + new_rtp[new_rtp_fill++] = ','; + memmove(new_rtp + new_rtp_fill, ffname, addlen); + new_rtp_fill += addlen - 1; + assert(new_rtp[new_rtp_fill] == NUL || new_rtp[new_rtp_fill] == ','); if (p_rtp[keep] != NUL) { - memmove(new_rtp + keep + addlen, p_rtp + keep, - oldlen - keep + 1); + memmove(new_rtp + new_rtp_fill, p_rtp + keep, oldlen - keep + 1); + new_rtp_fill += oldlen - keep; } if (afterlen > 0) { - STRCAT(new_rtp, ","); - STRCAT(new_rtp, afterdir); + assert(new_rtp[new_rtp_fill] == NUL); + new_rtp[new_rtp_fill++] = ','; + memmove(new_rtp + new_rtp_fill, afterdir, afterlen - 1); + new_rtp_fill += afterlen - 1; } - set_option_value((char_u *)"rtp", 0L, new_rtp, 0); + new_rtp[new_rtp_fill] = NUL; + set_option_value("rtp", 0L, new_rtp, 0); xfree(new_rtp); xfree(afterdir); } @@ -2567,7 +2587,7 @@ static void add_pack_plugin(char_u *fname, void *cookie) static const char *plugpat = "%s/plugin/**/*.vim"; // NOLINT static const char *ftpat = "%s/ftdetect/*.vim"; // NOLINT - size_t len = STRLEN(ffname) + STRLEN(ftpat); + size_t len = strlen(ffname) + STRLEN(ftpat); char_u *pat = try_malloc(len + 1); if (pat == NULL) { goto theend; @@ -2597,6 +2617,7 @@ static bool did_source_packages = false; // ":packloadall" // Find plugins in the package directories and source them. +// "eap" is NULL when invoked during startup. void ex_packloadall(exarg_T *eap) { if (!did_source_packages || (eap != NULL && eap->forceit)) { @@ -3684,12 +3705,12 @@ static void script_host_execute(char *name, exarg_T *eap) uint8_t *script = script_get(eap, eap->arg); if (!eap->skip) { - list_T *args = list_alloc(); + list_T *args = tv_list_alloc(); // script - list_append_string(args, script ? script : eap->arg, -1); + tv_list_append_string(args, (const char *)(script ? script : eap->arg), -1); // current range - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute", args); } @@ -3701,21 +3722,21 @@ static void script_host_execute_file(char *name, exarg_T *eap) uint8_t buffer[MAXPATHL]; vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false); - list_T *args = list_alloc(); + list_T *args = tv_list_alloc(); // filename - list_append_string(args, buffer, -1); + tv_list_append_string(args, (const char *)buffer, -1); // current range - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute_file", args); } static void script_host_do_range(char *name, exarg_T *eap) { - list_T *args = list_alloc(); - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); - list_append_string(args, eap->arg, -1); + list_T *args = tv_list_alloc(); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); + tv_list_append_string(args, (const char *)eap->arg, -1); (void)eval_call_provider(name, "do_range", args); } diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index c6389a0c8b..133c37cef4 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -72,7 +72,8 @@ #define ADDR_LOADED_BUFFERS 3 #define ADDR_BUFFERS 4 #define ADDR_TABS 5 -#define ADDR_QUICKFIX 6 +#define ADDR_TABS_RELATIVE 6 // Tab page that only relative +#define ADDR_QUICKFIX 7 #define ADDR_OTHER 99 typedef struct exarg exarg_T; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index bd3b8c204a..0fd4ae48be 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -67,6 +67,7 @@ #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" #include "nvim/shada.h" +#include "nvim/globals.h" static int quitmore = 0; static int ex_pressedreturn = FALSE; @@ -269,7 +270,7 @@ do_exmode ( /* * Execute a simple command line. Used for translated commands like "*". */ -int do_cmdline_cmd(char *cmd) +int do_cmdline_cmd(const char *cmd) { return do_cmdline((char_u *)cmd, NULL, NULL, DOCMD_NOWAIT|DOCMD_KEYTYPED); @@ -1522,8 +1523,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 = curwin->w_cursor.lnum; break; case ADDR_WINDOWS: - lnum = CURRENT_WIN_NR; - ea.line2 = lnum; + ea.line2 = CURRENT_WIN_NR; break; case ADDR_ARGUMENTS: ea.line2 = curwin->w_arg_idx + 1; @@ -1536,8 +1536,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 = curbuf->b_fnum; break; case ADDR_TABS: - lnum = CURRENT_TAB_NR; - ea.line2 = lnum; + ea.line2 = CURRENT_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + ea.line2 = 1; break; case ADDR_QUICKFIX: ea.line2 = qf_get_cur_valid_idx(&ea); @@ -1587,6 +1589,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, goto doend; } break; + case ADDR_TABS_RELATIVE: + errormsg = (char_u *)_(e_invrange); + goto doend; + break; case ADDR_ARGUMENTS: if (ARGCOUNT == 0) { ea.line1 = ea.line2 = 0; @@ -1973,6 +1979,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, case ADDR_TABS: ea.line2 = LAST_TAB_NR; break; + case ADDR_TABS_RELATIVE: + ea.line2 = 1; + break; case ADDR_ARGUMENTS: if (ARGCOUNT == 0) { ea.line1 = ea.line2 = 0; @@ -2028,11 +2037,11 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line1 = ea.line2; ea.line2 += n - 1; ++ea.addr_count; - /* - * Be vi compatible: no error message for out of range. - */ - if (ea.line2 > curbuf->b_ml.ml_line_count) + // Be vi compatible: no error message for out of range. + if (ea.addr_type == ADDR_LINES + && ea.line2 > curbuf->b_ml.ml_line_count) { ea.line2 = curbuf->b_ml.ml_line_count; + } } } @@ -2624,32 +2633,27 @@ int cmd_exists(const char *const name) * perfectly compatible with each other, but then the command line syntax * probably won't change that much -- webb. */ -char_u * -set_one_cmd_context ( +const char * set_one_cmd_context( expand_T *xp, - char_u *buff /* buffer for command string */ + const char *buff // buffer for command string ) { - char_u *p; - char_u *cmd, *arg; - int len = 0; + size_t len = 0; exarg_T ea; - int compl = EXPAND_NOTHING; - int delim; - int forceit = FALSE; - int usefilter = FALSE; /* filter instead of file name */ + int context = EXPAND_NOTHING; + int forceit = false; + int usefilter = false; // Filter instead of file name. ExpandInit(xp); - xp->xp_pattern = buff; - xp->xp_context = EXPAND_COMMANDS; /* Default until we get past command */ + xp->xp_pattern = (char_u *)buff; + xp->xp_context = EXPAND_COMMANDS; // Default until we get past command ea.argt = 0; - /* - * 2. skip comment lines and leading space, colons or bars - */ - for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++) - ; - xp->xp_pattern = cmd; + // 2. skip comment lines and leading space, colons or bars + const char *cmd; + for (cmd = buff; strchr(" \t:|", *cmd) != NULL; cmd++) { + } + xp->xp_pattern = (char_u *)cmd; if (*cmd == NUL) return NULL; @@ -2661,14 +2665,15 @@ set_one_cmd_context ( /* * 3. parse a range specifier of the form: addr [,addr] [;addr] .. */ - cmd = skip_range(cmd, &xp->xp_context); + cmd = (const char *)skip_range((const char_u *)cmd, &xp->xp_context); /* * 4. parse command */ - xp->xp_pattern = cmd; - if (*cmd == NUL) + xp->xp_pattern = (char_u *)cmd; + if (*cmd == NUL) { return NULL; + } if (*cmd == '"') { xp->xp_context = EXPAND_NOTHING; return NULL; @@ -2684,6 +2689,7 @@ set_one_cmd_context ( * do accept "keepmarks", "keepalt" and "keepjumps". * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' */ + const char *p; if (*cmd == 'k' && cmd[1] != 'e') { ea.cmdidx = CMD_k; p = cmd + 1; @@ -2706,20 +2712,21 @@ set_one_cmd_context ( } } // check for non-alpha command - if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) { + if (p == cmd && strchr("@*!=><&~#", *p) != NULL) { p++; } - len = (int)(p - cmd); + len = (size_t)(p - cmd); if (len == 0) { xp->xp_context = EXPAND_UNSUCCESSFUL; return NULL; } for (ea.cmdidx = (cmdidx_T)0; (int)ea.cmdidx < (int)CMD_SIZE; - ea.cmdidx = (cmdidx_T)((int)ea.cmdidx + 1)) - if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, cmd, - (size_t)len) == 0) + ea.cmdidx = (cmdidx_T)((int)ea.cmdidx + 1)) { + if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, cmd, len) == 0) { break; + } + } if (cmd[0] >= 'A' && cmd[0] <= 'Z') { while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card @@ -2736,16 +2743,15 @@ set_one_cmd_context ( return NULL; if (ea.cmdidx == CMD_SIZE) { - if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL) { + if (*cmd == 's' && strchr("cgriI", cmd[1]) != NULL) { ea.cmdidx = CMD_substitute; p = cmd + 1; } else if (cmd[0] >= 'A' && cmd[0] <= 'Z') { - ea.cmd = cmd; - p = find_ucmd(&ea, p, NULL, xp, - &compl - ); - if (p == NULL) - ea.cmdidx = CMD_SIZE; /* ambiguous user command */ + ea.cmd = (char_u *)cmd; + p = (const char *)find_ucmd(&ea, (char_u *)p, NULL, xp, &context); + if (p == NULL) { + ea.cmdidx = CMD_SIZE; // Ambiguous user command. + } } } if (ea.cmdidx == CMD_SIZE) { @@ -2768,16 +2774,17 @@ set_one_cmd_context ( ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt; } - arg = skipwhite(p); + const char *arg = (const char *)skipwhite((const char_u *)p); if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { - if (*arg == '>') { /* append */ - if (*++arg == '>') - ++arg; - arg = skipwhite(arg); - } else if (*arg == '!' && ea.cmdidx == CMD_write) { /* :w !filter */ - ++arg; - usefilter = TRUE; + if (*arg == '>') { // Append. + if (*++arg == '>') { + arg++; + } + arg = (const char *)skipwhite((const char_u *)arg); + } else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter + arg++; + usefilter = true; } } @@ -2790,23 +2797,24 @@ set_one_cmd_context ( } if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) { - while (*arg == *cmd) /* allow any number of '>' or '<' */ - ++arg; - arg = skipwhite(arg); + while (*arg == *cmd) { // allow any number of '>' or '<' + arg++; + } + arg = (const char *)skipwhite((const char_u *)arg); } /* Does command allow "+command"? */ if ((ea.argt & EDITCMD) && !usefilter && *arg == '+') { /* Check if we're in the +command */ p = arg + 1; - arg = skip_cmd_arg(arg, FALSE); + arg = (const char *)skip_cmd_arg((char_u *)arg, false); /* Still touching the command after '+'? */ if (*arg == NUL) return p; - /* Skip space(s) after +command to get to the real argument */ - arg = skipwhite(arg); + // Skip space(s) after +command to get to the real argument. + arg = (const char *)skipwhite((const char_u *)arg); } /* @@ -2835,19 +2843,18 @@ set_one_cmd_context ( } // no arguments allowed - if (!(ea.argt & EXTRA) && *arg != NUL - && vim_strchr((char_u *)"|\"", *arg) == NULL) { + if (!(ea.argt & EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) { return NULL; } /* Find start of last argument (argument just before cursor): */ p = buff; - xp->xp_pattern = p; - len = (int)STRLEN(buff); + xp->xp_pattern = (char_u *)p; + len = strlen(buff); while (*p && p < buff + len) { if (*p == ' ' || *p == TAB) { - /* argument starts after a space */ - xp->xp_pattern = ++p; + // Argument starts after a space. + xp->xp_pattern = (char_u *)++p; } else { if (*p == '\\' && *(p + 1) != NUL) ++p; /* skip over escaped character */ @@ -2857,25 +2864,26 @@ set_one_cmd_context ( if (ea.argt & XFILE) { int c; - int in_quote = FALSE; - char_u *bow = NULL; /* Beginning of word */ + int in_quote = false; + const char *bow = NULL; // Beginning of word. /* * Allow spaces within back-quotes to count as part of the argument * being expanded. */ - xp->xp_pattern = skipwhite(arg); - p = xp->xp_pattern; + xp->xp_pattern = skipwhite((const char_u *)arg); + p = (const char *)xp->xp_pattern; while (*p != NUL) { - if (has_mbyte) - c = mb_ptr2char(p); - else - c = *p; - if (c == '\\' && p[1] != NUL) - ++p; - else if (c == '`') { + if (has_mbyte) { + c = mb_ptr2char((const char_u *)p); + } else { + c = (uint8_t)(*p); + } + if (c == '\\' && p[1] != NUL) { + p++; + } else if (c == '`') { if (!in_quote) { - xp->xp_pattern = p; + xp->xp_pattern = (char_u *)p; bow = p + 1; } in_quote = !in_quote; @@ -2888,22 +2896,26 @@ set_one_cmd_context ( || ascii_iswhite(c)) { len = 0; /* avoid getting stuck when space is in 'isfname' */ while (*p != NUL) { - if (has_mbyte) - c = mb_ptr2char(p); - else + if (has_mbyte) { + c = mb_ptr2char((const char_u *)p); + } else { c = *p; - if (c == '`' || vim_isfilec_or_wc(c)) + } + if (c == '`' || vim_isfilec_or_wc(c)) { break; - if (has_mbyte) - len = (*mb_ptr2len)(p); - else + } + if (has_mbyte) { + len = (size_t)(*mb_ptr2len)((const char_u *)p); + } else { len = 1; + } mb_ptr_adv(p); } - if (in_quote) + if (in_quote) { bow = p; - else - xp->xp_pattern = p; + } else { + xp->xp_pattern = (char_u *)p; + } p -= len; } mb_ptr_adv(p); @@ -2913,8 +2925,9 @@ set_one_cmd_context ( * If we are still inside the quotes, and we passed a space, just * expand from there. */ - if (bow != NULL && in_quote) - xp->xp_pattern = bow; + if (bow != NULL && in_quote) { + xp->xp_pattern = (char_u *)bow; + } xp->xp_context = EXPAND_FILES; /* For a shell command more chars need to be escaped. */ @@ -2922,33 +2935,36 @@ set_one_cmd_context ( #ifndef BACKSLASH_IN_FILENAME xp->xp_shell = TRUE; #endif - /* When still after the command name expand executables. */ - if (xp->xp_pattern == skipwhite(arg)) + // When still after the command name expand executables. + if (xp->xp_pattern == skipwhite((const char_u *)arg)) { xp->xp_context = EXPAND_SHELLCMD; + } } - /* Check for environment variable */ - if (*xp->xp_pattern == '$' - ) { - for (p = xp->xp_pattern + 1; *p != NUL; ++p) - if (!vim_isIDc(*p)) + // Check for environment variable. + if (*xp->xp_pattern == '$') { + for (p = (const char *)xp->xp_pattern + 1; *p != NUL; p++) { + if (!vim_isIDc((uint8_t)(*p))) { break; + } + } if (*p == NUL) { xp->xp_context = EXPAND_ENV_VARS; - ++xp->xp_pattern; - /* Avoid that the assignment uses EXPAND_FILES again. */ - if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST) - compl = EXPAND_ENV_VARS; + xp->xp_pattern++; + // Avoid that the assignment uses EXPAND_FILES again. + if (context != EXPAND_USER_DEFINED && context != EXPAND_USER_LIST) { + context = EXPAND_ENV_VARS; + } } } /* Check for user names */ if (*xp->xp_pattern == '~') { - for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p) - ; - /* Complete ~user only if it partially matches a user name. - * A full match ~user<Tab> will be replaced by user's home - * directory i.e. something like ~user<Tab> -> /home/user/ */ - if (*p == NUL && p > xp->xp_pattern + 1 + for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) { + } + // Complete ~user only if it partially matches a user name. + // A full match ~user<Tab> will be replaced by user's home + // directory i.e. something like ~user<Tab> -> /home/user/ + if (*p == NUL && p > (const char *)xp->xp_pattern + 1 && match_user(xp->xp_pattern + 1) == 1) { xp->xp_context = EXPAND_USER; ++xp->xp_pattern; @@ -2978,7 +2994,7 @@ set_one_cmd_context ( break; case CMD_help: xp->xp_context = EXPAND_HELP; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; /* Command modifiers: return the argument. @@ -3021,13 +3037,14 @@ set_one_cmd_context ( if (*arg == NUL || !ends_excmd(*arg)) { /* also complete "None" */ set_context_in_echohl_cmd(xp, arg); - arg = skipwhite(skiptowhite(arg)); + arg = (const char *)skipwhite(skiptowhite((const char_u *)arg)); if (*arg != NUL) { xp->xp_context = EXPAND_NOTHING; - arg = skip_regexp(arg + 1, *arg, p_magic, NULL); + arg = (const char *)skip_regexp((char_u *)arg + 1, (uint8_t)(*arg), + p_magic, NULL); } } - return find_nextcmd(arg); + return (const char *)find_nextcmd((char_u *)arg); /* * All completion for the +cmdline_compl feature goes here. @@ -3036,15 +3053,15 @@ set_one_cmd_context ( case CMD_command: /* Check for attributes */ while (*arg == '-') { - arg++; /* Skip "-" */ - p = skiptowhite(arg); + arg++; // Skip "-". + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { - /* Cursor is still in the attribute */ - p = vim_strchr(arg, '='); + // Cursor is still in the attribute. + p = strchr(arg, '='); if (p == NULL) { - /* No "=", so complete attribute names */ + // No "=", so complete attribute names. xp->xp_context = EXPAND_USER_CMD_FLAGS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; return NULL; } @@ -3052,73 +3069,81 @@ set_one_cmd_context ( // their arguments as well. if (STRNICMP(arg, "complete", p - arg) == 0) { xp->xp_context = EXPAND_USER_COMPLETE; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } else if (STRNICMP(arg, "nargs", p - arg) == 0) { xp->xp_context = EXPAND_USER_NARGS; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } else if (STRNICMP(arg, "addr", p - arg) == 0) { xp->xp_context = EXPAND_USER_ADDR_TYPE; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } return NULL; } - arg = skipwhite(p); + arg = (const char *)skipwhite((char_u *)p); } - /* After the attributes comes the new command name */ - p = skiptowhite(arg); + // After the attributes comes the new command name. + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; } - /* And finally comes a normal command */ - return skipwhite(p); + // And finally comes a normal command. + return (const char *)skipwhite((const char_u *)p); case CMD_delcommand: xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_global: - case CMD_vglobal: - delim = *arg; /* get the delimiter */ - if (delim) - ++arg; /* skip delimiter if there is one */ + case CMD_vglobal: { + const int delim = (uint8_t)(*arg); // Get the delimiter. + if (delim) { + arg++; // Skip delimiter if there is one. + } - while (arg[0] != NUL && arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) - ++arg; - ++arg; + while (arg[0] != NUL && (uint8_t)arg[0] != delim) { + if (arg[0] == '\\' && arg[1] != NUL) { + arg++; + } + arg++; } if (arg[0] != NUL) return arg + 1; break; + } case CMD_and: - case CMD_substitute: - delim = *arg; + case CMD_substitute: { + const int delim = (uint8_t)(*arg); if (delim) { - /* skip "from" part */ - ++arg; - arg = skip_regexp(arg, delim, p_magic, NULL); + // Skip "from" part. + arg++; + arg = (const char *)skip_regexp((char_u *)arg, delim, p_magic, NULL); } - /* skip "to" part */ - while (arg[0] != NUL && arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) - ++arg; - ++arg; + // Skip "to" part. + while (arg[0] != NUL && (uint8_t)arg[0] != delim) { + if (arg[0] == '\\' && arg[1] != NUL) { + arg++; + } + arg++; } - if (arg[0] != NUL) /* skip delimiter */ - ++arg; - while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL) - ++arg; - if (arg[0] != NUL) + if (arg[0] != NUL) { // Skip delimiter. + arg++; + } + while (arg[0] && strchr("|\"#", arg[0]) == NULL) { + arg++; + } + if (arg[0] != NUL) { return arg; + } break; + } case CMD_isearch: case CMD_dsearch: case CMD_ilist: @@ -3128,36 +3153,40 @@ set_one_cmd_context ( case CMD_djump: case CMD_isplit: case CMD_dsplit: - arg = skipwhite(skipdigits(arg)); /* skip count */ - if (*arg == '/') { /* Match regexp, not just whole words */ - for (++arg; *arg && *arg != '/'; arg++) - if (*arg == '\\' && arg[1] != NUL) + // Skip count. + arg = (const char *)skipwhite(skipdigits((const char_u *)arg)); + if (*arg == '/') { // Match regexp, not just whole words. + for (++arg; *arg && *arg != '/'; arg++) { + if (*arg == '\\' && arg[1] != NUL) { arg++; + } + } if (*arg) { - arg = skipwhite(arg + 1); + arg = (const char *)skipwhite((const char_u *)arg + 1); - /* Check for trailing illegal characters */ - if (*arg && vim_strchr((char_u *)"|\"\n", *arg) == NULL) + // Check for trailing illegal characters. + if (*arg && strchr("|\"\n", *arg) == NULL) { xp->xp_context = EXPAND_NOTHING; - else + } else { return arg; + } } } break; case CMD_autocmd: - return set_context_in_autocmd(xp, arg, FALSE); + return (const char *)set_context_in_autocmd(xp, (char_u *)arg, false); case CMD_doautocmd: case CMD_doautoall: - return set_context_in_autocmd(xp, arg, TRUE); + return (const char *)set_context_in_autocmd(xp, (char_u *)arg, true); case CMD_set: - set_context_in_set_cmd(xp, arg, 0); + set_context_in_set_cmd(xp, (char_u *)arg, 0); break; case CMD_setglobal: - set_context_in_set_cmd(xp, arg, OPT_GLOBAL); + set_context_in_set_cmd(xp, (char_u *)arg, OPT_GLOBAL); break; case CMD_setlocal: - set_context_in_set_cmd(xp, arg, OPT_LOCAL); + set_context_in_set_cmd(xp, (char_u *)arg, OPT_LOCAL); break; case CMD_tag: case CMD_stag: @@ -3169,15 +3198,16 @@ set_one_cmd_context ( case CMD_tjump: case CMD_stjump: case CMD_ptjump: - if (*p_wop != NUL) + if (*p_wop != NUL) { xp->xp_context = EXPAND_TAGS_LISTFILES; - else + } else { xp->xp_context = EXPAND_TAGS; - xp->xp_pattern = arg; + } + xp->xp_pattern = (char_u *)arg; break; case CMD_augroup: xp->xp_context = EXPAND_AUGROUP; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_syntax: set_context_in_syntax_cmd(xp, arg); @@ -3194,20 +3224,21 @@ set_one_cmd_context ( case CMD_echoerr: case CMD_call: case CMD_return: - set_context_for_expression(xp, arg, ea.cmdidx); + set_context_for_expression(xp, (char_u *)arg, ea.cmdidx); break; case CMD_unlet: - while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) - arg = xp->xp_pattern + 1; + while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) { + arg = (const char *)xp->xp_pattern + 1; + } xp->xp_context = EXPAND_USER_VARS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_function: case CMD_delfunction: xp->xp_context = EXPAND_USER_FUNC; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_echohl: @@ -3222,33 +3253,37 @@ set_one_cmd_context ( set_context_in_cscope_cmd(xp, arg, ea.cmdidx); break; case CMD_sign: - set_context_in_sign_cmd(xp, arg); + set_context_in_sign_cmd(xp, (char_u *)arg); break; case CMD_bdelete: case CMD_bwipeout: case CMD_bunload: - while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) - arg = xp->xp_pattern + 1; - /*FALLTHROUGH*/ + while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) { + arg = (const char *)xp->xp_pattern + 1; + } + // FALLTHROUGH case CMD_buffer: case CMD_sbuffer: case CMD_checktime: xp->xp_context = EXPAND_BUFFERS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_USER: case CMD_USER_BUF: - if (compl != EXPAND_NOTHING) { - /* XFILE: file names are handled above */ + if (context != EXPAND_NOTHING) { + // XFILE: file names are handled above. if (!(ea.argt & XFILE)) { - if (compl == EXPAND_MENUS) - return set_context_in_menu_cmd(xp, cmd, arg, forceit); - if (compl == EXPAND_COMMANDS) + if (context == EXPAND_MENUS) { + return (const char *)set_context_in_menu_cmd(xp, (char_u *)cmd, + (char_u *)arg, forceit); + } else if (context == EXPAND_COMMANDS) { return arg; - if (compl == EXPAND_MAPPINGS) - return set_context_in_map_cmd(xp, (char_u *)"map", - arg, forceit, FALSE, FALSE, CMD_map); - /* Find start of last argument. */ + } else if (context == EXPAND_MAPPINGS) { + return (const char *)set_context_in_map_cmd( + xp, (char_u *)"map", (char_u *)arg, forceit, false, false, + CMD_map); + } + // Find start of last argument. p = arg; while (*p) { if (*p == ' ') @@ -3258,9 +3293,9 @@ set_one_cmd_context ( ++p; /* skip over escaped character */ mb_ptr_adv(p); } - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; } - xp->xp_context = compl; + xp->xp_context = context; } break; case CMD_map: case CMD_noremap: @@ -3272,8 +3307,8 @@ set_one_cmd_context ( case CMD_lmap: case CMD_lnoremap: case CMD_smap: case CMD_snoremap: case CMD_xmap: case CMD_xnoremap: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - FALSE, FALSE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, false, false, ea.cmdidx); case CMD_unmap: case CMD_nunmap: case CMD_vunmap: @@ -3283,18 +3318,18 @@ set_one_cmd_context ( case CMD_lunmap: case CMD_sunmap: case CMD_xunmap: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - FALSE, TRUE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, false, true, ea.cmdidx); case CMD_abbreviate: case CMD_noreabbrev: case CMD_cabbrev: case CMD_cnoreabbrev: case CMD_iabbrev: case CMD_inoreabbrev: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - TRUE, FALSE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, true, false, ea.cmdidx); case CMD_unabbreviate: case CMD_cunabbrev: case CMD_iunabbrev: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - TRUE, TRUE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, true, true, ea.cmdidx); case CMD_menu: case CMD_noremenu: case CMD_unmenu: case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu: case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu: @@ -3304,47 +3339,49 @@ set_one_cmd_context ( case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu: case CMD_tmenu: case CMD_tunmenu: case CMD_popup: case CMD_emenu: - return set_context_in_menu_cmd(xp, cmd, arg, forceit); + return (const char *)set_context_in_menu_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit); case CMD_colorscheme: xp->xp_context = EXPAND_COLORS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_compiler: xp->xp_context = EXPAND_COMPILER; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_ownsyntax: xp->xp_context = EXPAND_OWNSYNTAX; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_setfiletype: xp->xp_context = EXPAND_FILETYPE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_packadd: xp->xp_context = EXPAND_PACKADD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; #ifdef HAVE_WORKING_LIBINTL case CMD_language: - p = skiptowhite(arg); + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { xp->xp_context = EXPAND_LANGUAGE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; } else { - if ( STRNCMP(arg, "messages", p - arg) == 0 - || STRNCMP(arg, "ctype", p - arg) == 0 - || STRNCMP(arg, "time", p - arg) == 0) { + if (strncmp(arg, "messages", p - arg) == 0 + || strncmp(arg, "ctype", p - arg) == 0 + || strncmp(arg, "time", p - arg) == 0) { xp->xp_context = EXPAND_LOCALES; - xp->xp_pattern = skipwhite(p); - } else + xp->xp_pattern = skipwhite((const char_u *)p); + } else { xp->xp_context = EXPAND_NOTHING; + } } break; #endif @@ -3353,16 +3390,16 @@ set_one_cmd_context ( break; case CMD_behave: xp->xp_context = EXPAND_BEHAVE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_history: xp->xp_context = EXPAND_HISTORY; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_syntime: xp->xp_context = EXPAND_SYNTIME; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; @@ -3381,10 +3418,9 @@ set_one_cmd_context ( * Also skip white space and ":" characters. * Returns the "cmd" pointer advanced to beyond the range. */ -char_u * -skip_range ( - char_u *cmd, - int *ctx /* pointer to xp_context or NULL */ +char_u *skip_range( + const char_u *cmd, + int *ctx // pointer to xp_context or NULL ) { unsigned delim; @@ -3409,7 +3445,7 @@ skip_range ( while (*cmd == ':') cmd = skipwhite(cmd + 1); - return cmd; + return (char_u *)cmd; } /* @@ -3459,6 +3495,11 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = CURRENT_TAB_NR; break; + case ADDR_TABS_RELATIVE: + EMSG(_(e_invrange)); + cmd = NULL; + goto error; + break; case ADDR_QUICKFIX: lnum = qf_get_cur_valid_idx(eap); break; @@ -3493,6 +3534,11 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = LAST_TAB_NR; break; + case ADDR_TABS_RELATIVE: + EMSG(_(e_invrange)); + cmd = NULL; + goto error; + break; case ADDR_QUICKFIX: lnum = qf_get_size(eap); if (lnum == 0) { @@ -3641,6 +3687,9 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = CURRENT_TAB_NR; break; + case ADDR_TABS_RELATIVE: + lnum = 1; + break; case ADDR_QUICKFIX: lnum = qf_get_cur_valid_idx(eap); break; @@ -3655,7 +3704,12 @@ static linenr_T get_address(exarg_T *eap, n = 1; else n = getdigits(&cmd); - if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) { + + if (addr_type == ADDR_TABS_RELATIVE) { + EMSG(_(e_invrange)); + cmd = NULL; + goto error; + } else if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) { lnum = compute_buffer_local_count( addr_type, lnum, (i == '-') ? -1 * n : n); } else { @@ -3777,6 +3831,9 @@ static char_u *invalid_range(exarg_T *eap) return (char_u *)_(e_invrange); } break; + case ADDR_TABS_RELATIVE: + // Do nothing + break; case ADDR_QUICKFIX: assert(eap->line2 >= 0); if (eap->line2 != 1 && (size_t)eap->line2 > qf_get_size(eap)) { @@ -4285,6 +4342,89 @@ static int getargopt(exarg_T *eap) return OK; } +/// Handle the argument for a tabpage related ex command. +/// Returns a tabpage number. +/// When an error is encountered then eap->errmsg is set. +static int get_tabpage_arg(exarg_T *eap) +{ + int tab_number = 0; + int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1; + + if (eap->arg && *eap->arg != NUL) { + char_u *p = eap->arg; + char_u *p_save; + int relative = 0; // argument +N/-N means: go to N places to the + // right/left relative to the current position. + + if (*p == '-') { + relative = -1; + p++; + } else if (*p == '+') { + relative = 1; + p++; + } + + p_save = p; + tab_number = getdigits(&p); + + if (relative == 0) { + if (STRCMP(p, "$") == 0) { + tab_number = LAST_TAB_NR; + } else if (p == p_save || *p_save == '-' || *p != NUL + || tab_number > LAST_TAB_NR) { + // No numbers as argument. + eap->errmsg = e_invarg; + goto theend; + } + } else { + if (*p_save == NUL) { + tab_number = 1; + } + else if (p == p_save || *p_save == '-' || *p != NUL || tab_number == 0) { + // No numbers as argument. + eap->errmsg = e_invarg; + goto theend; + } + tab_number = tab_number * relative + tabpage_index(curtab); + if (!unaccept_arg0 && relative == -1) { + --tab_number; + } + } + if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR) { + eap->errmsg = e_invarg; + } + } else if (eap->addr_count > 0) { + if (unaccept_arg0 && eap->line2 == 0) { + eap->errmsg = e_invrange; + tab_number = 0; + } else { + tab_number = eap->line2; + if (!unaccept_arg0 && **eap->cmdlinep == '-') { + --tab_number; + if (tab_number < unaccept_arg0) { + eap->errmsg = e_invarg; + } + } + } + } else { + switch (eap->cmdidx) { + case CMD_tabnext: + tab_number = tabpage_index(curtab) + 1; + if (tab_number > LAST_TAB_NR) + tab_number = 1; + break; + case CMD_tabmove: + tab_number = LAST_TAB_NR; + break; + default: + tab_number = tabpage_index(curtab); + } + } + +theend: + return tab_number; +} + /* * ":abbreviate" and friends. */ @@ -4472,14 +4612,15 @@ int ends_excmd(int c) FUNC_ATTR_CONST * Return the next command, after the first '|' or '\n'. * Return NULL if not found. */ -char_u *find_nextcmd(char_u *p) +char_u *find_nextcmd(const char_u *p) { while (*p != '|' && *p != '\n') { - if (*p == NUL) + if (*p == NUL) { return NULL; - ++p; + } + p++; } - return p + 1; + return (char_u *)p + 1; } /* @@ -5775,7 +5916,7 @@ static void ex_quit(exarg_T *eap) // Refuse to quit when locked or when the buffer in the last window is // being closed (can only happen in autocommands). if (curbuf_locked() - || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_closing)) { + || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0)) { return; } @@ -5833,11 +5974,12 @@ static void ex_quit_all(exarg_T *eap) text_locked_msg(); return; } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - /* Refuse to quit when locked or when the buffer in the last window is - * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) + apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, curbuf); + // Refuse to quit when locked or when the buffer in the last window is + // being closed (can only happen in autocommands). + if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) { return; + } exiting = true; if (eap->forceit || !check_changed_any(false, false)) { @@ -5937,8 +6079,9 @@ static void ex_tabclose(exarg_T *eap) else if (first_tabpage->tp_next == NULL) EMSG(_("E784: Cannot close last tab page")); else { - if (eap->addr_count > 0) { - tp = find_tabpage((int)eap->line2); + int tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + tp = find_tabpage(tab_number); if (tp == NULL) { beep_flush(); return; @@ -5946,44 +6089,46 @@ static void ex_tabclose(exarg_T *eap) if (tp != curtab) { tabpage_close_other(tp, eap->forceit); return; + } else if (!text_locked() && !curbuf_locked()) { + tabpage_close(eap->forceit); } } - if (!text_locked() - && !curbuf_locked() - ) - tabpage_close(eap->forceit); } } -/* - * ":tabonly": close all tab pages except the current one - */ +/// ":tabonly": close all tab pages except the current one static void ex_tabonly(exarg_T *eap) { - if (cmdwin_type != 0) + if (cmdwin_type != 0) { cmdwin_result = K_IGNORE; - else if (first_tabpage->tp_next == NULL) - MSG(_("Already only one tab page")); - else { - if (eap->addr_count > 0) - goto_tabpage(eap->line2); - /* Repeat this up to a 1000 times, because autocommands may mess - * up the lists. */ - for (int done = 0; done < 1000; ++done) { - FOR_ALL_TABS(tp) { - if (tp->tp_topframe != topframe) { - tabpage_close_other(tp, eap->forceit); - /* if we failed to close it quit */ - if (valid_tabpage(tp)) - done = 1000; - /* start over, "tp" is now invalid */ + } else if (first_tabpage->tp_next == NULL) { + MSG(_("Already only one tab page")); + } else { + int tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + goto_tabpage(tab_number); + // Repeat this up to a 1000 times, because autocommands may + // mess up the lists. + for (int done = 0; done < 1000; done++) { + FOR_ALL_TAB_WINDOWS(tp, wp) { + assert(wp != aucmd_win); + } + FOR_ALL_TABS(tp) { + if (tp->tp_topframe != topframe) { + tabpage_close_other(tp, eap->forceit); + // if we failed to close it quit + if (valid_tabpage(tp)) { + done = 1000; + } + // start over, "tp" is now invalid + break; + } + } + assert(first_tabpage); + if (first_tabpage->tp_next == NULL) { break; } } - assert(first_tabpage); - if (first_tabpage->tp_next == NULL) { - break; - } } } } @@ -6128,11 +6273,12 @@ static void ex_exit(exarg_T *eap) text_locked_msg(); return; } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - /* Refuse to quit when locked or when the buffer in the last window is - * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) + apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, curbuf); + // Refuse to quit when locked or when the buffer in the last window is + // being closed (can only happen in autocommands). + if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) { return; + } // if more files or windows we won't exit if (check_more(false, eap->forceit) == OK && only_one_window()) { @@ -6471,6 +6617,8 @@ void tabpage_new(void) */ static void ex_tabnext(exarg_T *eap) { + int tab_number; + switch (eap->cmdidx) { case CMD_tabfirst: case CMD_tabrewind: @@ -6481,66 +6629,48 @@ static void ex_tabnext(exarg_T *eap) break; case CMD_tabprevious: case CMD_tabNext: - goto_tabpage(eap->addr_count == 0 ? -1 : -(int)eap->line2); - break; - default: /* CMD_tabnext */ - goto_tabpage(eap->addr_count == 0 ? 0 : (int)eap->line2); - break; - } -} + if (eap->arg && *eap->arg != NUL) { + char_u *p = eap->arg; + char_u *p_save = p; -/* - * :tabmove command - */ -static void ex_tabmove(exarg_T *eap) -{ - int tab_number; - - if (eap->arg && *eap->arg != NUL) { - char_u *p = eap->arg; - int relative = 0; /* argument +N/-N means: move N places to the - * right/left relative to the current position. */ - - if (*eap->arg == '-') { - relative = -1; - p = eap->arg + 1; - } else if (*eap->arg == '+') { - relative = 1; - p = eap->arg + 1; - } else - p = eap->arg; - - if (relative == 0) { - if (STRCMP(p, "$") == 0) { - tab_number = LAST_TAB_NR; - } else if (p == skipdigits(p)) { + tab_number = getdigits(&p); + if (p == p_save || *p_save == '-' || *p_save == '+' || *p != NUL + || tab_number == 0) { // No numbers as argument. eap->errmsg = e_invarg; return; - } else { - tab_number = getdigits(&p); } } else { - if (*p != NUL) { - tab_number = getdigits(&p); - } else { + if (eap->addr_count == 0) { tab_number = 1; - } - tab_number = tab_number * relative + tabpage_index(curtab); - if (relative == -1) { - --tab_number; + } else { + tab_number = eap->line2; + if (tab_number < 1) { + eap->errmsg = e_invrange; + return; + } } } - } else if (eap->addr_count != 0) { - tab_number = eap->line2; - if (**eap->cmdlinep == '-') { - --tab_number; + goto_tabpage(-tab_number); + break; + default: // CMD_tabnext + tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + goto_tabpage(tab_number); } - } else { - tab_number = LAST_TAB_NR; + break; } +} - tabpage_move(tab_number); +/* + * :tabmove command + */ +static void ex_tabmove(exarg_T *eap) +{ + int tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + tabpage_move(tab_number); + } } /* @@ -6692,7 +6822,7 @@ do_exedit ( int ms = msg_scroll; if (eap->nextcmd != NULL) { - stuffReadbuff(eap->nextcmd); + stuffReadbuff((const char *)eap->nextcmd); eap->nextcmd = NULL; } @@ -7622,7 +7752,7 @@ static void ex_mkrc(exarg_T *eap) /* When using 'viewdir' may have to create the directory. */ if (using_vdir && !os_isdir(p_vdir)) { - vim_mkdir_emsg(p_vdir, 0755); + vim_mkdir_emsg((const char *)p_vdir, 0755); } fd = open_exfile((char_u *) fname, eap->forceit, WRITEBIN); @@ -7734,10 +7864,17 @@ static void ex_mkrc(exarg_T *eap) xfree(viewFile); } -int vim_mkdir_emsg(char_u *name, int prot) +/// Try creating a directory, give error message on failure +/// +/// @param[in] name Directory to create. +/// @param[in] prot Directory permissions. +/// +/// @return OK in case of success, FAIL otherwise. +int vim_mkdir_emsg(const char *const name, const int prot) + FUNC_ATTR_NONNULL_ALL { int ret; - if ((ret = os_mkdir((char *)name, prot)) != 0) { + if ((ret = os_mkdir(name, prot)) != 0) { EMSG3(_(e_mkdir), name, os_strerror(ret)); return FAIL; } @@ -8314,8 +8451,8 @@ eval_vars ( *usedlen = 1; return NULL; } - result = list_find_str(get_vim_var_list(VV_OLDFILES), - (long)i); + result = (char_u *)tv_list_find_str(get_vim_var_list(VV_OLDFILES), + i - 1); if (result == NULL) { *errormsg = (char_u *)""; return NULL; @@ -9245,8 +9382,8 @@ static int ses_put_fname(FILE *fd, char_u *name, unsigned *flagp) *p = '/'; } - /* escape special characters */ - p = vim_strsave_fnameescape(sname, FALSE); + // Escape special characters. + p = (char_u *)vim_strsave_fnameescape((const char *)sname, false); xfree(sname); /* write the result */ @@ -9381,18 +9518,18 @@ void dialog_msg(char_u *buff, char *format, char_u *fname) static void ex_behave(exarg_T *eap) { if (STRCMP(eap->arg, "mswin") == 0) { - set_option_value((char_u *)"selection", 0L, (char_u *)"exclusive", 0); - set_option_value((char_u *)"selectmode", 0L, (char_u *)"mouse,key", 0); - set_option_value((char_u *)"mousemodel", 0L, (char_u *)"popup", 0); - set_option_value((char_u *)"keymodel", 0L, - (char_u *)"startsel,stopsel", 0); + set_option_value("selection", 0L, "exclusive", 0); + set_option_value("selectmode", 0L, "mouse,key", 0); + set_option_value("mousemodel", 0L, "popup", 0); + set_option_value("keymodel", 0L, "startsel,stopsel", 0); } else if (STRCMP(eap->arg, "xterm") == 0) { - set_option_value((char_u *)"selection", 0L, (char_u *)"inclusive", 0); - set_option_value((char_u *)"selectmode", 0L, (char_u *)"", 0); - set_option_value((char_u *)"mousemodel", 0L, (char_u *)"extend", 0); - set_option_value((char_u *)"keymodel", 0L, (char_u *)"", 0); - } else + set_option_value("selection", 0L, "inclusive", 0); + set_option_value("selectmode", 0L, "", 0); + set_option_value("mousemodel", 0L, "extend", 0); + set_option_value("keymodel", 0L, "", 0); + } else { EMSG2(_(e_invarg2), eap->arg); + } } /* @@ -9506,8 +9643,9 @@ void filetype_maybe_enable(void) */ static void ex_setfiletype(exarg_T *eap) { - if (!did_filetype) - set_option_value((char_u *)"filetype", 0L, eap->arg, OPT_LOCAL); + if (!did_filetype) { + set_option_value("filetype", 0L, (char *)eap->arg, OPT_LOCAL); + } } static void ex_digraphs(exarg_T *eap) @@ -9593,7 +9731,8 @@ static void ex_match(exarg_T *eap) c = *end; *end = NUL; - match_add(curwin, g, p + 1, 10, id, NULL, NULL); + match_add(curwin, (const char *)g, (const char *)p + 1, 10, id, + NULL, NULL); xfree(g); *end = c; } diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index 4def4cbbae..cff350de08 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -2,6 +2,7 @@ #define NVIM_EX_DOCMD_H #include "nvim/ex_cmds_defs.h" +#include "nvim/globals.h" // flags for do_cmdline() #define DOCMD_VERBOSE 0x01 // included command in error message diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 4bb6f97035..65112c4dd8 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1,6 +1,8 @@ -/* - * ex_eval.c: functions for Ex command line for the +eval feature. - */ +// TODO(ZyX-I): move to eval/executor + +/// @file ex_eval.c +/// +/// Functions for Ex command line for the +eval feature. #include <assert.h> #include <stdbool.h> #include <stdint.h> @@ -12,6 +14,7 @@ #include "nvim/ex_eval.h" #include "nvim/charset.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/message.h" @@ -19,8 +22,6 @@ #include "nvim/regexp.h" #include "nvim/strings.h" - - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_eval.c.generated.h" #endif @@ -57,12 +58,14 @@ * is an error exception.) - The macros can be defined as expressions checking * for a variable that is allowed to be changed during execution of a script. */ -/* Values used for the Vim release. */ -# define THROW_ON_ERROR TRUE -# define THROW_ON_ERROR_TRUE -# define THROW_ON_INTERRUPT TRUE -# define THROW_ON_INTERRUPT_TRUE +// Values used for the Vim release. +#define THROW_ON_ERROR true +#define THROW_ON_ERROR_TRUE +#define THROW_ON_INTERRUPT true +#define THROW_ON_INTERRUPT_TRUE + +#define discard_pending_return(p) tv_free((typval_T *)(p)) /* * When several errors appear in a row, setting "force_abort" is delayed until @@ -779,7 +782,6 @@ void report_discard_pending(int pending, void *value) */ void ex_if(exarg_T *eap) { - int error; int skip; int result; struct condstack *cstack = eap->cstack; @@ -800,6 +802,7 @@ void ex_if(exarg_T *eap) 1] & CSF_ACTIVE)); + bool error; result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); if (!skip && !error) { @@ -844,7 +847,6 @@ void ex_endif(exarg_T *eap) */ void ex_else(exarg_T *eap) { - int error; int skip; int result; struct condstack *cstack = eap->cstack; @@ -901,6 +903,7 @@ void ex_else(exarg_T *eap) } if (eap->cmdidx == CMD_elseif) { + bool error; result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); /* When throwing error exceptions, we want to throw always the first * of several errors in a row. This is what actually happens when @@ -925,7 +928,7 @@ void ex_else(exarg_T *eap) */ void ex_while(exarg_T *eap) { - int error; + bool error; int skip; int result; struct condstack *cstack = eap->cstack; @@ -1147,23 +1150,25 @@ void ex_endwhile(exarg_T *eap) */ void ex_throw(exarg_T *eap) { - char_u *arg = eap->arg; - char_u *value; + const char *arg = (const char *)eap->arg; + char *value; - if (*arg != NUL && *arg != '|' && *arg != '\n') - value = eval_to_string_skip(arg, &eap->nextcmd, eap->skip); - else { + if (*arg != NUL && *arg != '|' && *arg != '\n') { + value = eval_to_string_skip(arg, (const char **)&eap->nextcmd, + (bool)eap->skip); + } else { EMSG(_(e_argreq)); value = NULL; } - /* On error or when an exception is thrown during argument evaluation, do - * not throw. */ + // On error or when an exception is thrown during argument evaluation, do + // not throw. if (!eap->skip && value != NULL) { - if (throw_exception(value, ET_USER, NULL) == FAIL) + if (throw_exception((char_u *)value, ET_USER, NULL) == FAIL) { xfree(value); - else + } else { do_throw(eap->cstack); + } } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index d99c8d02f7..8810204c03 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -8,6 +8,7 @@ #include <stdlib.h> #include <inttypes.h> +#include "nvim/assert.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/arabic.h" @@ -578,7 +579,7 @@ static int command_line_execute(VimState *state, int key) } if (vim_ispathsep(ccline.cmdbuff[s->j]) #ifdef BACKSLASH_IN_FILENAME - && vim_strchr(" *?[{`$%#", ccline.cmdbuff[s->j + 1]) + && strchr(" *?[{`$%#", ccline.cmdbuff[s->j + 1]) == NULL #endif ) { @@ -621,11 +622,9 @@ static int command_line_execute(VimState *state, int key) // CTRL-\ CTRL-N goes to Normal mode, CTRL-\ CTRL-G goes to Insert // mode when 'insertmode' is set, CTRL-\ e prompts for an expression. if (s->c == Ctrl_BSL) { - ++no_mapping; - ++allow_keys; + no_mapping++; s->c = plain_vgetc(); - --no_mapping; - --allow_keys; + no_mapping--; // CTRL-\ e doesn't work when obtaining an expression, unless it // is in a mapping. if (s->c != Ctrl_N && s->c != Ctrl_G && (s->c != 'e' @@ -962,7 +961,7 @@ static int command_line_handle_key(CommandLineState *s) return command_line_not_changed(s); case Ctrl_HAT: - if (map_to_exists_mode((char_u *)"", LANGMAP, false)) { + if (map_to_exists_mode("", LANGMAP, false)) { // ":lmap" mappings exists, toggle use of mappings. State ^= LANGMAP; if (s->b_im_ptr != NULL) { @@ -1887,8 +1886,7 @@ getexmodeline ( msg_putchar(' '); } } - ++no_mapping; - ++allow_keys; + no_mapping++; /* * Get the line, one character at a time. @@ -2078,8 +2076,7 @@ redraw: } } - --no_mapping; - --allow_keys; + no_mapping--; /* make following messages go to the next line */ msg_didout = FALSE; @@ -2557,19 +2554,22 @@ void cmdline_paste_str(char_u *s, int literally) else while (*s != NUL) { cv = *s; - if (cv == Ctrl_V && s[1]) - ++s; - if (has_mbyte) - c = mb_cptr2char_adv(&s); - else + if (cv == Ctrl_V && s[1]) { + s++; + } + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&s); + } else { c = *s++; + } if (cv == Ctrl_V || c == ESC || c == Ctrl_C || c == CAR || c == NL || c == Ctrl_L #ifdef UNIX || c == intr_char #endif - || (c == Ctrl_BSL && *s == Ctrl_N)) + || (c == Ctrl_BSL && *s == Ctrl_N)) { stuffcharReadbuff(Ctrl_V); + } stuffcharReadbuff(c); } } @@ -3124,9 +3124,10 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o #endif } #ifdef BACKSLASH_IN_FILENAME - p = vim_strsave_fnameescape(files[i], FALSE); + p = (char_u *)vim_strsave_fnameescape((const char *)files[i], false); #else - p = vim_strsave_fnameescape(files[i], xp->xp_shell); + p = (char_u *)vim_strsave_fnameescape((const char *)files[i], + xp->xp_shell); #endif xfree(files[i]); files[i] = p; @@ -3156,42 +3157,49 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o } } -/* - * Escape special characters in "fname" for when used as a file name argument - * after a Vim command, or, when "shell" is non-zero, a shell command. - * Returns the result in allocated memory. - */ -char_u *vim_strsave_fnameescape(char_u *fname, int shell) FUNC_ATTR_NONNULL_RET +/// Escape special characters in a file name for use as a command argument +/// +/// @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. +/// +/// @return [allocated] escaped file name. +char *vim_strsave_fnameescape(const char *const fname, const bool shell) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char_u *p; #ifdef BACKSLASH_IN_FILENAME -#define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`%#'\"|!<") - char_u buf[20]; +#define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<" + char_u buf[sizeof(PATH_ESC_CHARS)]; int j = 0; - /* Don't escape '[', '{' and '!' if they are in 'isfname'. */ - for (p = PATH_ESC_CHARS; *p != NUL; ++p) - if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec(*p)) - buf[j++] = *p; + // 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; + } + } buf[j] = NUL; - p = vim_strsave_escaped(fname, buf); + char *p = (char *)vim_strsave_escaped((const char_u *)fname, + (const char_u *)buf); #else #define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<") #define SHELL_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<>();&") - p = vim_strsave_escaped(fname, shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS); + char *p = (char *)vim_strsave_escaped( + (const char_u *)fname, (shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS)); if (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_u *s = vim_strsave_escaped(p, (char_u *)"!"); + // 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, + (const char_u *)"!"); xfree(p); p = s; } #endif - /* '>' and '+' are special at the start of some commands, e.g. ":edit" and - * ":write". "cd -" has a special meaning. */ + // '>' and '+' are special at the start of some commands, e.g. ":edit" and + // ":write". "cd -" has a special meaning. if (*p == '>' || *p == '+' || (*p == '-' && p[1] == NUL)) { - escape_fname(&p); + escape_fname((char_u **)&p); } return p; @@ -3628,7 +3636,6 @@ set_cmd_context ( ) { int old_char = NUL; - char_u *nextcomm; /* * Avoid a UMR warning from Purify, only save the character if it has been @@ -3637,7 +3644,7 @@ set_cmd_context ( if (col < len) old_char = str[col]; str[col] = NUL; - nextcomm = str; + const char *nextcomm = (const char *)str; if (use_ccline && ccline.cmdfirstc == '=') { // pass CMD_SIZE because there is no real command @@ -3646,9 +3653,11 @@ set_cmd_context ( xp->xp_context = ccline.xp_context; xp->xp_pattern = ccline.cmdbuff; xp->xp_arg = ccline.xp_arg; - } else - while (nextcomm != NULL) + } else { + while (nextcomm != NULL) { nextcomm = set_one_cmd_context(xp, nextcomm); + } + } /* Store the string here so that call_user_expand_func() can get to them * easily. */ @@ -4201,9 +4210,11 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u keep; garray_T ga; - retstr = call_user_expand_func(call_func_retstr, xp, num_file, file); - if (retstr == NULL) + retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, + num_file, file); + if (retstr == NULL) { return FAIL; + } ga_init(&ga, (int)sizeof(char *), 3); for (s = retstr; *s != NUL; s = e) { @@ -4241,9 +4252,11 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) listitem_T *li; garray_T ga; - retlist = call_user_expand_func(call_func_retlist, xp, num_file, file); - if (retlist == NULL) + retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, + num_file, file); + if (retlist == NULL) { return FAIL; + } ga_init(&ga, (int)sizeof(char *), 3); /* Loop over the items in the list. */ @@ -4253,7 +4266,7 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) GA_APPEND(char_u *, &ga, vim_strsave(li->li_tv.vval.v_string)); } - list_unref(retlist); + tv_list_unref(retlist); *file = ga.ga_data; *num_file = ga.ga_len; @@ -4549,7 +4562,7 @@ static inline void hist_free_entry(histentry_T *hisptr) FUNC_ATTR_NONNULL_ALL { xfree(hisptr->hisstr); - list_unref(hisptr->additional_elements); + tv_list_unref(hisptr->additional_elements); clear_hist_entry(hisptr); } @@ -4605,7 +4618,7 @@ in_history ( history[type][last_i] = history[type][i]; last_i = i; } - list_unref(list); + tv_list_unref(list); history[type][i].hisnum = ++hisnum[type]; history[type][i].hisstr = str; history[type][i].timestamp = os_time(); @@ -4627,7 +4640,7 @@ in_history ( /// /// @return Any value from HistoryType enum, including HIST_INVALID. May not /// return HIST_DEFAULT unless return_default is true. -HistoryType get_histtype(const char_u *const name, const size_t len, +HistoryType get_histtype(const char *const name, const size_t len, const bool return_default) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -4681,8 +4694,8 @@ add_to_history ( * down, only lines that were added. */ if (histype == HIST_SEARCH && in_map) { - if (maptick == last_maptick) { - /* Current line is from the same mapping, remove it */ + if (maptick == last_maptick && hisidx[HIST_SEARCH] >= 0) { + // Current line is from the same mapping, remove it hisptr = &history[HIST_SEARCH][hisidx[HIST_SEARCH]]; hist_free_entry(hisptr); --hisnum[histype]; @@ -5020,7 +5033,7 @@ void ex_history(exarg_T *eap) while (ASCII_ISALPHA(*end) || vim_strchr((char_u *)":=@>/?", *end) != NULL) end++; - histype1 = get_histtype(arg, end - arg, false); + histype1 = get_histtype((const char *)arg, end - arg, false); if (histype1 == HIST_INVALID) { if (STRNICMP(arg, "all", end - arg) == 0) { histype1 = 0; @@ -5177,7 +5190,7 @@ static int ex_window(void) // Create empty command-line buffer. buf_open_scratch(0, "[Command Line]"); // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer. - set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); curwin->w_p_rl = cmdmsg_rl; cmdmsg_rl = false; curbuf->b_p_ma = true; @@ -5195,7 +5208,7 @@ static int ex_window(void) add_map((char_u *)"<buffer> <Tab> <C-X><C-V>", INSERT); add_map((char_u *)"<buffer> <Tab> a<C-X><C-V>", NORMAL); } - set_option_value((char_u *)"ft", 0L, (char_u *)"vim", OPT_LOCAL); + set_option_value("ft", 0L, "vim", OPT_LOCAL); } /* Reset 'textwidth' after setting 'filetype' (the Vim filetype plugin @@ -5227,10 +5240,8 @@ static int ex_window(void) invalidate_botline(); redraw_later(SOME_VALID); - /* Save the command line info, can be used recursively. */ - save_ccline = ccline; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; + // Save the command line info, can be used recursively. + save_cmdline(&save_ccline); /* No Ex mode here! */ exmode_active = 0; @@ -5264,8 +5275,8 @@ static int ex_window(void) /* Restore KeyTyped in case it is modified by autocommands */ KeyTyped = save_KeyTyped; - /* Restore the command line info. */ - ccline = save_ccline; + // Restore the command line info. + restore_cmdline(&save_ccline); cmdwin_type = 0; exmode_active = save_exmode; @@ -5281,18 +5292,18 @@ static int ex_window(void) cmdwin_result = Ctrl_C; /* Set the new command line from the cmdline buffer. */ xfree(ccline.cmdbuff); - if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { /* :qa[!] typed */ - char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!"; + if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { // :qa[!] typed + const char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!"; if (histtype == HIST_CMD) { - /* Execute the command directly. */ - ccline.cmdbuff = vim_strsave((char_u *)p); + // Execute the command directly. + ccline.cmdbuff = (char_u *)xstrdup(p); cmdwin_result = CAR; } else { - /* First need to cancel what we were doing. */ + // First need to cancel what we were doing. ccline.cmdbuff = NULL; stuffcharReadbuff(':'); - stuffReadbuff((char_u *)p); + stuffReadbuff(p); stuffcharReadbuff(CAR); } } else if (cmdwin_result == K_XF2) { /* :qa typed */ diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 24eebdc303..5a1ca5213a 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -1,7 +1,7 @@ #ifndef NVIM_EX_GETLN_H #define NVIM_EX_GETLN_H -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" /* Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. */ diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index b73d9944ce..9592235905 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -322,8 +322,11 @@ vim_findfile_init ( drive[0] = path[0]; drive[1] = ':'; drive[2] = NUL; - if (vim_FullName(drive, ff_expand_buffer, MAXPATHL, TRUE) == FAIL) + if (vim_FullName((const char *)drive, (char *)ff_expand_buffer, MAXPATHL, + true) + == FAIL) { goto error_return; + } path += 2; } else #endif @@ -1549,14 +1552,14 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope) assert(false); } - dict_add_nr_str(dict, "scope", 0L, (char_u *)buf); - dict_add_nr_str(dict, "cwd", 0L, (char_u *)new_dir); - dict_set_keys_readonly(dict); + tv_dict_add_str(dict, S_LEN("scope"), buf); + tv_dict_add_str(dict, S_LEN("cwd"), new_dir); + tv_dict_set_keys_readonly(dict); apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, NULL); - dict_clear(dict); + tv_dict_clear(dict); recursive = false; } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 873c15ff4a..c1b8203ed1 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -200,18 +200,14 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) { int msg_scroll_save; - if (msg_silent != 0) + if (msg_silent != 0) { return; - msg_add_fname(buf, name); /* put file name in IObuff with quotes */ - /* If it's extremely long, truncate it. */ - if (STRLEN(IObuff) > IOSIZE - 80) - IObuff[IOSIZE - 80] = NUL; - STRCAT(IObuff, s); - /* - * For the first message may have to start a new line. - * For further ones overwrite the previous one, reset msg_scroll before - * calling filemess(). - */ + } + add_quoted_fname((char *)IObuff, IOSIZE - 80, buf, (const char *)name); + xstrlcat((char *)IObuff, (const char *)s, IOSIZE); + // For the first message may have to start a new line. + // For further ones overwrite the previous one, reset msg_scroll before + // calling filemess(). msg_scroll_save = msg_scroll; if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) msg_scroll = FALSE; @@ -428,7 +424,7 @@ readfile ( } if (!read_buffer && !read_stdin) { - perm = os_getperm(fname); + perm = os_getperm((const char *)fname); #ifdef UNIX // On Unix it is possible to read a directory, so we have to // check for it before os_open(). @@ -614,10 +610,12 @@ readfile ( return FAIL; } #ifdef UNIX - /* Set swap file protection bits after creating it. */ + // Set swap file protection bits after creating it. if (swap_mode > 0 && curbuf->b_ml.ml_mfp != NULL - && curbuf->b_ml.ml_mfp->mf_fname != NULL) - (void)os_setperm(curbuf->b_ml.ml_mfp->mf_fname, (long)swap_mode); + && curbuf->b_ml.ml_mfp->mf_fname != NULL) { + (void)os_setperm((const char *)curbuf->b_ml.ml_mfp->mf_fname, + (long)swap_mode); + } #endif } @@ -1798,8 +1796,8 @@ failed: } if (!filtering && !(flags & READ_DUMMY)) { - msg_add_fname(curbuf, sfname); /* fname in IObuff with quotes */ - c = FALSE; + add_quoted_fname((char *)IObuff, IOSIZE, curbuf, (const char *)sfname); + c = false; #ifdef UNIX # ifdef S_ISFIFO @@ -2256,9 +2254,16 @@ buf_write ( int len; linenr_T lnum; long nchars; - char_u *errmsg = NULL; - int errmsg_allocated = FALSE; - char_u *errnum = NULL; +#define SET_ERRMSG_NUM(num, msg) \ + errnum = num, errmsg = msg, errmsgarg = 0 +#define SET_ERRMSG_ARG(msg, error) \ + errnum = NULL, errmsg = msg, errmsgarg = error +#define SET_ERRMSG(msg) \ + errnum = NULL, errmsg = msg, errmsgarg = 0 + const char *errnum = NULL; + char *errmsg = NULL; + int errmsgarg = 0; + bool errmsg_allocated = false; char_u *buffer; char_u smallbuf[SMBUFSIZE]; char_u *backup_ext; @@ -2280,7 +2285,6 @@ buf_write ( /* writing everything */ int whole = (start == 1 && end == buf->b_ml.ml_line_count); linenr_T old_line_count = buf->b_ml.ml_line_count; - int attr; int fileformat; int write_bin; struct bw_info write_info; /* info for buf_write_bytes() */ @@ -2575,13 +2579,11 @@ buf_write ( perm = file_info_old.stat.st_mode; if (!S_ISREG(file_info_old.stat.st_mode)) { /* not a file */ if (S_ISDIR(file_info_old.stat.st_mode)) { - errnum = (char_u *)"E502: "; - errmsg = (char_u *)_("is a directory"); + SET_ERRMSG_NUM("E502", _("is a directory")); goto fail; } if (os_nodetype((char *)fname) != NODE_WRITABLE) { - errnum = (char_u *)"E503: "; - errmsg = (char_u *)_("is not a file or writable device"); + SET_ERRMSG_NUM("E503", _("is not a file or writable device")); goto fail; } /* It's a device of some kind (or a fifo) which we can write to @@ -2597,8 +2599,7 @@ buf_write ( */ c = os_nodetype((char *)fname); if (c == NODE_OTHER) { - errnum = (char_u *)"E503: "; - errmsg = (char_u *)_("is not a file or writable device"); + SET_ERRMSG_NUM("E503", _("is not a file or writable device")); goto fail; } if (c == NODE_WRITABLE) { @@ -2606,12 +2607,11 @@ buf_write ( newfile = TRUE; perm = -1; } else { - perm = os_getperm(fname); - if (perm < 0) - newfile = TRUE; - else if (os_isdir(fname)) { - errnum = (char_u *)"E502: "; - errmsg = (char_u *)_("is a directory"); + perm = os_getperm((const char *)fname); + if (perm < 0) { + newfile = true; + } else if (os_isdir(fname)) { + SET_ERRMSG_NUM("E502", _("is a directory")); goto fail; } if (overwriting) { @@ -2630,11 +2630,9 @@ buf_write ( if (!forceit && file_readonly) { if (vim_strchr(p_cpo, CPO_FWRITE) != NULL) { - errnum = (char_u *)"E504: "; - errmsg = (char_u *)_(err_readonly); + SET_ERRMSG_NUM("E504", _(err_readonly)); } else { - errnum = (char_u *)"E505: "; - errmsg = (char_u *)_("is read-only (add ! to override)"); + SET_ERRMSG_NUM("E505", _("is read-only (add ! to override)")); } goto fail; } @@ -2870,9 +2868,9 @@ buf_write ( xfree(backup); backup = NULL; } else { - /* set file protection same as original file, but - * strip s-bit */ - (void)os_setperm(backup, perm & 0777); + // set file protection same as original file, but + // strip s-bit. + (void)os_setperm((const char *)backup, perm & 0777); #ifdef UNIX /* @@ -2883,7 +2881,8 @@ buf_write ( */ if (file_info_new.stat.st_gid != file_info_old.stat.st_gid && os_fchown(bfd, -1, file_info_old.stat.st_gid) != 0) { - os_setperm(backup, (perm & 0707) | ((perm & 07) << 3)); + os_setperm((const char *)backup, + (perm & 0707) | ((perm & 07) << 3)); } # ifdef HAVE_SELINUX mch_copy_sec(fname, backup); @@ -2901,23 +2900,27 @@ buf_write ( while ((write_info.bw_len = read_eintr(fd, copybuf, BUFSIZE)) > 0) { if (buf_write_bytes(&write_info) == FAIL) { - errmsg = (char_u *)_( - "E506: Can't write to backup file (add ! to override)"); + SET_ERRMSG(_( + "E506: Can't write to backup file (add ! to override)")); break; } os_breakcheck(); if (got_int) { - errmsg = (char_u *)_(e_interr); + SET_ERRMSG(_(e_interr)); break; } } - if (close(bfd) < 0 && errmsg == NULL) - errmsg = (char_u *)_( - "E507: Close error for backup file (add ! to override)"); - if (write_info.bw_len < 0) - errmsg = (char_u *)_( - "E508: Can't read file for backup (add ! to override)"); + int error; + if ((error = os_close(bfd)) != 0 && errmsg == NULL) { + SET_ERRMSG_ARG(_("E507: Close error for backup file " + "(add ! to override): %s"), + error); + } + if (write_info.bw_len < 0) { + SET_ERRMSG(_( + "E508: Can't read file for backup (add ! to override)")); + } #ifdef UNIX set_file_time(backup, file_info_old.stat.st_atim.tv_sec, @@ -2934,18 +2937,19 @@ buf_write ( } } nobackup: - close(fd); /* ignore errors for closing read file */ + os_close(fd); // Ignore errors for closing read file. xfree(copybuf); - if (backup == NULL && errmsg == NULL) - errmsg = (char_u *)_( - "E509: Cannot create backup file (add ! to override)"); - /* ignore errors when forceit is TRUE */ + if (backup == NULL && errmsg == NULL) { + SET_ERRMSG(_( + "E509: Cannot create backup file (add ! to override)")); + } + // Ignore errors when forceit is TRUE. if ((some_error || errmsg != NULL) && !forceit) { retval = FAIL; goto fail; } - errmsg = NULL; + SET_ERRMSG(NULL); } else { char_u *dirp; char_u *p; @@ -2960,8 +2964,7 @@ nobackup: * anyway, thus we need an extra check here. */ if (file_readonly && vim_strchr(p_cpo, CPO_FWRITE) != NULL) { - errnum = (char_u *)"E504: "; - errmsg = (char_u *)_(err_readonly); + SET_ERRMSG_NUM("E504", _(err_readonly)); goto fail; } @@ -3025,7 +3028,7 @@ nobackup: } } if (backup == NULL && !forceit) { - errmsg = (char_u *)_("E510: Can't make backup file (add ! to override)"); + SET_ERRMSG(_("E510: Can't make backup file (add ! to override)")); goto fail; } } @@ -3037,8 +3040,8 @@ nobackup: && file_info_old.stat.st_uid == getuid() && vim_strchr(p_cpo, CPO_FWRITE) == NULL) { perm |= 0200; - (void)os_setperm(fname, perm); - made_writable = TRUE; + (void)os_setperm((const char *)fname, perm); + made_writable = true; } #endif @@ -3066,7 +3069,7 @@ nobackup: && !(exiting && backup != NULL)) { ml_preserve(buf, FALSE); if (got_int) { - errmsg = (char_u *)_(e_interr); + SET_ERRMSG(_(e_interr)); goto restore_backup; } } @@ -3137,8 +3140,8 @@ nobackup: */ if (*p_ccv != NUL) { wfname = vim_tempname(); - if (wfname == NULL) { /* Can't write without a tempfile! */ - errmsg = (char_u *)_("E214: Can't find temp file for writing"); + if (wfname == NULL) { // Can't write without a tempfile! + SET_ERRMSG(_("E214: Can't find temp file for writing")); goto restore_backup; } } @@ -3150,8 +3153,8 @@ nobackup: && wfname == fname ) { if (!forceit) { - errmsg = (char_u *)_( - "E213: Cannot convert (add ! to write without conversion)"); + SET_ERRMSG(_( + "E213: Cannot convert (add ! to write without conversion)")); goto restore_backup; } notconverted = TRUE; @@ -3186,11 +3189,10 @@ nobackup: if ((!newfile && os_fileinfo_hardlinks(&file_info) > 1) || (os_fileinfo_link((char *)fname, &file_info) && !os_fileinfo_id_equal(&file_info, &file_info_old))) { - errmsg = (char_u *)_("E166: Can't open linked file for writing"); - } else + SET_ERRMSG(_("E166: Can't open linked file for writing")); + } else { #endif - { - errmsg = (char_u *)_("E212: Can't open file for writing"); + SET_ERRMSG_ARG(_("E212: Can't open file for writing: %s"), fd); if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL && perm >= 0) { #ifdef UNIX @@ -3208,7 +3210,9 @@ nobackup: os_remove((char *)wfname); continue; } +#ifdef UNIX } +#endif } restore_backup: @@ -3250,7 +3254,7 @@ restore_backup: xfree(wfname); goto fail; } - errmsg = NULL; + SET_ERRMSG(NULL); write_info.bw_fd = fd; @@ -3370,7 +3374,6 @@ restore_backup: nchars += len; } -#if defined(UNIX) // On many journalling file systems there is a bug that causes both the // original and the backup file to be lost when halting the system right // after writing the file. That's because only the meta-data is @@ -3379,11 +3382,11 @@ restore_backup: // For a device do try the fsync() but don't complain if it does not work // (could be a pipe). // If the 'fsync' option is FALSE, don't fsync(). Useful for laptops. - if (p_fs && os_fsync(fd) != 0 && !device) { - errmsg = (char_u *)_("E667: Fsync failed"); + int error; + if (p_fs && (error = os_fsync(fd)) != 0 && !device) { + SET_ERRMSG_ARG(_("E667: Fsync failed: %s"), error); end = 0; } -#endif #ifdef HAVE_SELINUX /* Probably need to set the security context. */ @@ -3402,8 +3405,9 @@ restore_backup: || file_info.stat.st_uid != file_info_old.stat.st_uid || file_info.stat.st_gid != file_info_old.stat.st_gid) { os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); - if (perm >= 0) /* set permission again, may have changed */ - (void)os_setperm(wfname, perm); + if (perm >= 0) { // Set permission again, may have changed. + (void)os_setperm((const char *)wfname, perm); + } } buf_set_file_id(buf); } else if (!buf->file_id_valid) { @@ -3412,8 +3416,8 @@ restore_backup: } #endif - if (close(fd) != 0) { - errmsg = (char_u *)_("E512: Close failed"); + if ((error = os_close(fd)) != 0) { + SET_ERRMSG_ARG(_("E512: Close failed: %s"), error); end = 0; } @@ -3421,8 +3425,9 @@ restore_backup: if (made_writable) perm &= ~0200; /* reset 'w' bit for security reasons */ #endif - if (perm >= 0) /* set perm. of new file same as old file */ - (void)os_setperm(wfname, perm); + if (perm >= 0) { // Set perm. of new file same as old file. + (void)os_setperm((const char *)wfname, perm); + } #ifdef HAVE_ACL /* Probably need to set the ACL before changing the user (can't set the * ACL on a file the user doesn't own). */ @@ -3449,21 +3454,24 @@ restore_backup: if (end == 0) { if (errmsg == NULL) { if (write_info.bw_conv_error) { - if (write_info.bw_conv_error_lnum == 0) - errmsg = (char_u *)_( - "E513: write error, conversion failed (make 'fenc' empty to override)"); - else { - errmsg_allocated = TRUE; - errmsg = xmalloc(300); - vim_snprintf((char *)errmsg, 300, - _("E513: write error, conversion failed in line %" PRId64 + if (write_info.bw_conv_error_lnum == 0) { + SET_ERRMSG(_( + "E513: write error, conversion failed " + "(make 'fenc' empty to override)")); + } else { + errmsg_allocated = true; + SET_ERRMSG(xmalloc(300)); + vim_snprintf( + errmsg, 300, + _("E513: write error, conversion failed in line %" PRIdLINENR " (make 'fenc' empty to override)"), - (int64_t)write_info.bw_conv_error_lnum); + write_info.bw_conv_error_lnum); } - } else if (got_int) - errmsg = (char_u *)_(e_interr); - else - errmsg = (char_u *)_("E514: write error (file system full?)"); + } else if (got_int) { + SET_ERRMSG(_(e_interr)); + } else { + SET_ERRMSG(_("E514: write error (file system full?)")); + } } /* @@ -3518,8 +3526,8 @@ restore_backup: fname = sfname; /* use shortname now, for the messages */ #endif if (!filtering) { - msg_add_fname(buf, fname); /* put fname in IObuff with quotes */ - c = FALSE; + add_quoted_fname((char *)IObuff, IOSIZE, buf, (const char *)fname); + c = false; if (write_info.bw_conv_error) { STRCAT(IObuff, _(" CONVERSION ERROR")); c = TRUE; @@ -3628,7 +3636,7 @@ restore_backup: close(empty_fd); } if (org != NULL) { - os_setperm((char_u *)org, os_getperm(fname) & 0777); + os_setperm(org, os_getperm((const char *)fname) & 0777); xfree(org); } } @@ -3668,33 +3676,32 @@ nofail: #endif if (errmsg != NULL) { - int numlen = errnum != NULL ? (int)STRLEN(errnum) : 0; - - attr = hl_attr(HLF_E); /* set highlight for error messages */ - msg_add_fname(buf, + // - 100 to save some space for further error message #ifndef UNIX - sfname + add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)sfname); #else - fname + add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)fname); #endif - ); /* put file name in IObuff with quotes */ - if (STRLEN(IObuff) + STRLEN(errmsg) + numlen >= IOSIZE) - IObuff[IOSIZE - STRLEN(errmsg) - numlen - 1] = NUL; - /* If the error message has the form "is ...", put the error number in - * front of the file name. */ if (errnum != NULL) { - STRMOVE(IObuff + numlen, IObuff); - memmove(IObuff, errnum, (size_t)numlen); + if (errmsgarg != 0) { + emsgf("%s: %s%s: %s", errnum, IObuff, errmsg, os_strerror(errmsgarg)); + } else { + emsgf("%s: %s%s", errnum, IObuff, errmsg); + } + } else if (errmsgarg != 0) { + emsgf(errmsg, os_strerror(errmsgarg)); + } else { + emsgf(errmsg); } - STRCAT(IObuff, errmsg); - emsg(IObuff); - if (errmsg_allocated) + if (errmsg_allocated) { xfree(errmsg); + } retval = FAIL; if (end == 0) { + const int attr = hl_attr(HLF_E); // Set highlight for error messages. MSG_PUTS_ATTR(_("\nWARNING: Original file may be lost or damaged\n"), - attr | MSG_HIST); + attr | MSG_HIST); MSG_PUTS_ATTR(_( "don't quit the editor until the file is successfully written!"), attr | MSG_HIST); @@ -3754,6 +3761,9 @@ nofail: got_int |= prev_got_int; return retval; +#undef SET_ERRMSG +#undef SET_ERRMSG_ARG +#undef SET_ERRMSG_NUM } /* @@ -3797,16 +3807,25 @@ static int set_rw_fname(char_u *fname, char_u *sfname) return OK; } -/* - * Put file name into IObuff with quotes. - */ -void msg_add_fname(buf_T *buf, char_u *fname) +/// Put file name into the specified buffer with quotes +/// +/// Replaces home directory at the start with `~`. +/// +/// @param[out] ret_buf Buffer to save results to. +/// @param[in] buf_len ret_buf length. +/// @param[in] buf buf_T file name is coming from. +/// @param[in] fname File name to write. +static void add_quoted_fname(char *const ret_buf, const size_t buf_len, + const buf_T *const buf, const char *fname) + FUNC_ATTR_NONNULL_ARG(1) { - if (fname == NULL) - fname = (char_u *)"-stdin-"; - home_replace(buf, fname, IObuff + 1, IOSIZE - 4, TRUE); - IObuff[0] = '"'; - STRCAT(IObuff, "\" "); + if (fname == NULL) { + fname = "-stdin-"; + } + ret_buf[0] = '"'; + home_replace(buf, (const char_u *)fname, (char_u *)ret_buf + 1, + (int)buf_len - 4, true); + xstrlcat(ret_buf, "\" ", buf_len); } /// Append message for text mode to IObuff. @@ -4548,9 +4567,9 @@ int put_time(FILE *fd, time_t time_) /// os_rename() only works if both files are on the same file system, this /// function will (attempts to?) copy the file across if rename fails -- webb -// +/// /// @return -1 for failure, 0 for success -int vim_rename(char_u *from, char_u *to) +int vim_rename(const char_u *from, const char_u *to) { int fd_in; int fd_out; @@ -4569,10 +4588,12 @@ int vim_rename(char_u *from, char_u *to) * the file name differs we need to go through a temp file. */ if (fnamecmp(from, to) == 0) { - if (p_fic && STRCMP(path_tail(from), path_tail(to)) != 0) + if (p_fic && (STRCMP(path_tail((char_u *)from), path_tail((char_u *)to)) + != 0)) { use_tmp_file = true; - else + } else { return 0; + } } // Fail if the "from" file doesn't exist. Avoids that "to" is deleted. @@ -4638,9 +4659,9 @@ int vim_rename(char_u *from, char_u *to) /* * Rename() failed, try copying the file. */ - perm = os_getperm(from); + perm = os_getperm((const char *)from); #ifdef HAVE_ACL - /* For systems that support ACL: get the ACL from the original file. */ + // For systems that support ACL: get the ACL from the original file. acl = mch_get_acl(from); #endif fd_in = os_open((char *)from, O_RDONLY, 0); @@ -4688,8 +4709,8 @@ int vim_rename(char_u *from, char_u *to) errmsg = _("E210: Error reading \"%s\""); to = from; } -#ifndef UNIX /* for Unix os_open() already set the permission */ - os_setperm(to, perm); +#ifndef UNIX // For Unix os_open() already set the permission. + os_setperm((const char *)to, perm); #endif #ifdef HAVE_ACL mch_set_acl(to, acl); @@ -5200,7 +5221,7 @@ void forward_slash(char_u *fname) { char_u *p; - if (path_with_url(fname)) { + if (path_with_url((const char *)fname)) { return; } for (p = fname; *p != NUL; p++) { @@ -5261,7 +5282,7 @@ static void vim_maketempdir(void) /// Delete "name" and everything in it, recursively. /// @param name The path which should be deleted. /// @return 0 for success, -1 if some file was not deleted. -int delete_recursive(char_u *name) +int delete_recursive(const char *name) { int result = 0; @@ -5275,7 +5296,7 @@ int delete_recursive(char_u *name) EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS | EW_DODOT | EW_EMPTYOK) == OK) { for (int i = 0; i < file_count; i++) { - if (delete_recursive(files[i]) != 0) { + if (delete_recursive((const char *)files[i]) != 0) { result = -1; } } @@ -5285,9 +5306,9 @@ int delete_recursive(char_u *name) } xfree(exp); - os_rmdir((char *)name); + os_rmdir(name); } else { - result = os_remove((char *)name) == 0 ? 0 : -1; + result = os_remove(name) == 0 ? 0 : -1; } return result; @@ -5299,7 +5320,7 @@ void vim_deltempdir(void) if (vim_tempdir != NULL) { // remove the trailing path separator path_tail(vim_tempdir)[-1] = NUL; - delete_recursive(vim_tempdir); + delete_recursive((const char *)vim_tempdir); xfree(vim_tempdir); vim_tempdir = NULL; } @@ -6464,6 +6485,12 @@ win_found: win_remove(curwin, NULL); aucmd_win_used = false; last_status(false); // may need to remove last status line + + if (!valid_tabpage_win(curtab)) { + // no valid window in current tabpage + close_tabpage(curtab); + } + restore_snapshot(SNAP_AUCMD_IDX, false); (void)win_comp_pos(); // recompute window positions unblock_autocmds(); diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 7c0283971e..d810aee0ce 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2232,32 +2232,51 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, * before where we started looking, extend it. If it * starts at another line, update nested folds to keep * their position, compensating for the new fd_top. */ - if (fp->fd_top >= startlnum && fp->fd_top != firstlnum) { - if (fp->fd_top > firstlnum) - /* like lines are inserted */ + if (fp->fd_top == firstlnum) { + // We have found a fold beginning exactly where we want one. + } else if (fp->fd_top >= startlnum) { + if (fp->fd_top > firstlnum) { + // We will move the start of this fold up, hence we move all + // nested folds (with relative line numbers) down. foldMarkAdjustRecurse(&fp->fd_nested, - (linenr_T)0, (linenr_T)MAXLNUM, - (long)(fp->fd_top - firstlnum), 0L); - else - /* like lines are deleted */ + (linenr_T)0, (linenr_T)MAXLNUM, + (long)(fp->fd_top - firstlnum), 0L); + } else { + // Will move fold down, move nested folds relatively up. foldMarkAdjustRecurse(&fp->fd_nested, - (linenr_T)0, - (long)(firstlnum - fp->fd_top - 1), - (linenr_T)MAXLNUM, - (long)(fp->fd_top - firstlnum)); + (linenr_T)0, + (long)(firstlnum - fp->fd_top - 1), + (linenr_T)MAXLNUM, + (long)(fp->fd_top - firstlnum)); + } fp->fd_len += fp->fd_top - firstlnum; fp->fd_top = firstlnum; - fold_changed = TRUE; - } else if (flp->start != 0 && lvl == level - && fp->fd_top != firstlnum) { - /* Existing fold that includes startlnum must stop - * if we find the start of a new fold at the same - * level. Split it. Delete contained folds at - * this point to split them too. */ - foldRemove(&fp->fd_nested, flp->lnum - fp->fd_top, - flp->lnum - fp->fd_top); + fold_changed = true; + } else if ((flp->start != 0 && lvl == level) + || (firstlnum != startlnum)) { + // Before there was a fold spanning from above startlnum to below + // firstlnum. This fold is valid above startlnum (because we are + // not updating that range), but there is now a break in it. + // If the break is because we are now forced to start a new fold + // at the level "level" at line fline->lnum, then we need to + // split the fold at fline->lnum. + // If the break is because the range [startlnum, firstlnum) is + // now at a lower indent than "level", we need to split the fold + // in this range. + // Any splits have to be done recursively. + linenr_T breakstart; + linenr_T breakend; + if (firstlnum != startlnum) { + breakstart = startlnum; + breakend = firstlnum; + } else { + breakstart = flp->lnum; + breakend = flp->lnum; + } + foldRemove(&fp->fd_nested, breakstart - fp->fd_top, + breakend - fp->fd_top); i = (int)(fp - (fold_T *)gap->ga_data); - foldSplit(gap, i, flp->lnum, flp->lnum - 1); + foldSplit(gap, i, breakstart, breakend - 1); fp = (fold_T *)gap->ga_data + i + 1; /* If using the "marker" or "syntax" method, we * need to continue until the end of the fold is @@ -2267,6 +2286,16 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, || getlevel == foldlevelSyntax) finish = TRUE; } + if (fp->fd_top == startlnum && concat) { + i = (int)(fp - (fold_T *)gap->ga_data); + if (i != 0) { + fp2 = fp - 1; + if (fp2->fd_top + fp2->fd_len == fp->fd_top) { + foldMerge(fp2, gap, fp); + fp = fp2; + } + } + } break; } if (fp->fd_top >= startlnum) { @@ -2507,6 +2536,8 @@ static void foldSplit(garray_T *gap, int i, linenr_T top, linenr_T bot) fp = (fold_T *)gap->ga_data + i; fp[1].fd_top = bot + 1; + // check for wrap around (MAXLNUM, and 32bit) + assert(fp[1].fd_top > bot); fp[1].fd_len = fp->fd_len - (fp[1].fd_top - fp->fd_top); fp[1].fd_flags = fp->fd_flags; fp[1].fd_small = MAYBE; @@ -2555,34 +2586,35 @@ static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot) { fold_T *fp = NULL; - if (bot < top) - return; /* nothing to do */ + if (bot < top) { + return; // nothing to do + } for (;; ) { - /* Find fold that includes top or a following one. */ + // Find fold that includes top or a following one. if (foldFind(gap, top, &fp) && fp->fd_top < top) { - /* 2: or 3: need to delete nested folds */ + // 2: or 3: need to delete nested folds foldRemove(&fp->fd_nested, top - fp->fd_top, bot - fp->fd_top); - if (fp->fd_top + fp->fd_len > bot + 1) { - /* 3: need to split it. */ + if (fp->fd_top + fp->fd_len - 1 > bot) { + // 3: need to split it. foldSplit(gap, (int)(fp - (fold_T *)gap->ga_data), top, bot); } else { - /* 2: truncate fold at "top". */ + // 2: truncate fold at "top". fp->fd_len = top - fp->fd_top; } - fold_changed = TRUE; + fold_changed = true; continue; } if (fp >= (fold_T *)(gap->ga_data) + gap->ga_len || fp->fd_top > bot) { - /* 6: Found a fold below bot, can stop looking. */ + // 6: Found a fold below bot, can stop looking. break; } if (fp->fd_top >= top) { - /* Found an entry below top. */ - fold_changed = TRUE; + // Found an entry below top. + fold_changed = true; if (fp->fd_top + fp->fd_len - 1 > bot) { - /* 5: Make fold that includes bot start below bot. */ + // 5: Make fold that includes bot start below bot. foldMarkAdjustRecurse(&fp->fd_nested, (linenr_T)0, (long)(bot - fp->fd_top), (linenr_T)MAXLNUM, (long)(fp->fd_top - bot - 1)); @@ -2591,11 +2623,159 @@ static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot) break; } - /* 4: Delete completely contained fold. */ - deleteFoldEntry(gap, (int)(fp - (fold_T *)gap->ga_data), TRUE); + // 4: Delete completely contained fold. + deleteFoldEntry(gap, (int)(fp - (fold_T *)gap->ga_data), true); + } + } +} + +// foldMoveRange() {{{2 +static void reverse_fold_order(garray_T *gap, size_t start, size_t end) +{ + for (; start < end; start++, end--) { + fold_T *left = (fold_T *)gap->ga_data + start; + fold_T *right = (fold_T *)gap->ga_data + end; + fold_T tmp = *left; + *left = *right; + *right = tmp; + } +} + +// Move folds within the inclusive range "line1" to "line2" to after "dest" +// require "line1" <= "line2" <= "dest" +// +// There are the following situations for the first fold at or below line1 - 1. +// 1 2 3 4 +// 1 2 3 4 +// line1 2 3 4 +// 2 3 4 5 6 7 +// line2 3 4 5 6 7 +// 3 4 6 7 8 9 +// dest 4 7 8 9 +// 4 7 8 10 +// 4 7 8 10 +// +// In the following descriptions, "moved" means moving in the buffer, *and* in +// the fold array. +// Meanwhile, "shifted" just means moving in the buffer. +// 1. not changed +// 2. truncated above line1 +// 3. length reduced by line2 - line1, folds starting between the end of 3 and +// dest are truncated and shifted up +// 4. internal folds moved (from [line1, line2] to dest) +// 5. moved to dest. +// 6. truncated below line2 and moved. +// 7. length reduced by line2 - dest, folds starting between line2 and dest are +// removed, top is moved down by move_len. +// 8. truncated below dest and shifted up. +// 9. shifted up +// 10. not changed +static void truncate_fold(fold_T *fp, linenr_T end) +{ + // I want to stop *at here*, foldRemove() stops *above* top + end += 1; + foldRemove(&fp->fd_nested, end - fp->fd_top, MAXLNUM); + fp->fd_len = end - fp->fd_top; +} + +#define FOLD_END(fp) ((fp)->fd_top + (fp)->fd_len - 1) +#define VALID_FOLD(fp, gap) ((fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len)) +#define FOLD_INDEX(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data))) +void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, + const linenr_T dest) +{ + fold_T *fp; + const linenr_T range_len = line2 - line1 + 1; + const linenr_T move_len = dest - line2; + const bool at_start = foldFind(gap, line1 - 1, &fp); + + if (at_start) { + if (FOLD_END(fp) > dest) { + // Case 4 -- don't have to change this fold, but have to move nested + // folds. + foldMoveRange(&fp->fd_nested, line1 - fp->fd_top, line2 - + fp->fd_top, dest - fp->fd_top); + return; + } else if (FOLD_END(fp) > line2) { + // Case 3 -- Remove nested folds between line1 and line2 & reduce the + // length of fold by "range_len". + // Folds after this one must be dealt with. + foldMarkAdjustRecurse(&fp->fd_nested, line1 - fp->fd_top, + line2 - fp->fd_top, MAXLNUM, -range_len); + fp->fd_len -= range_len; + } else { + // Case 2 -- truncate fold *above* line1. + // Folds after this one must be dealt with. + truncate_fold(fp, line1 - 1); + } + // Look at the next fold, and treat that one as if it were the first after + // "line1" (because now it is). + fp = fp + 1; + } + + if (!VALID_FOLD(fp, gap) || fp->fd_top > dest) { + // No folds after "line1" and before "dest" + // Case 10. + return; + } else if (fp->fd_top > line2) { + for (; VALID_FOLD(fp, gap) && FOLD_END(fp) <= dest; fp++) { + // Case 9. (for all case 9's) -- shift up. + fp->fd_top -= range_len; + } + if (VALID_FOLD(fp, gap) && fp->fd_top <= dest) { + // Case 8. -- ensure truncated at dest, shift up + truncate_fold(fp, dest); + fp->fd_top -= range_len; + } + return; + } else if (FOLD_END(fp) > dest) { + // Case 7 -- remove nested folds and shrink + foldMarkAdjustRecurse(&fp->fd_nested, line2 + 1 - fp->fd_top, + dest - fp->fd_top, MAXLNUM, -move_len); + fp->fd_len -= move_len; + fp->fd_top += move_len; + return; + } + + // Case 5 or 6: changes rely on whether there are folds between the end of + // this fold and "dest". + size_t move_start = FOLD_INDEX(fp, gap); + size_t move_end = 0, dest_index = 0; + for (; VALID_FOLD(fp, gap) && fp->fd_top <= dest; fp++) { + if (fp->fd_top <= line2) { + // 5, or 6 + if (FOLD_END(fp) > line2) { + // 6, truncate before moving + truncate_fold(fp, line2); + } + fp->fd_top += move_len; + continue; + } + + // Record index of the first fold after the moved range. + if (move_end == 0) { + move_end = FOLD_INDEX(fp, gap); } + + if (FOLD_END(fp) > dest) { + truncate_fold(fp, dest); + } + + fp->fd_top -= range_len; } + dest_index = FOLD_INDEX(fp, gap); + + // All folds are now correct, but they are not necessarily in the correct + // order. + // We have to swap folds in the range [move_end, dest_index) with those in + // the range [move_start, move_end). + reverse_fold_order(gap, move_start, dest_index - 1); + reverse_fold_order(gap, move_start, move_start + dest_index - move_end - 1); + reverse_fold_order(gap, move_start + dest_index - move_end, dest_index - 1); } +#undef FOLD_END +#undef VALID_FOLD +#undef FOLD_INDEX /* foldMerge() {{{2 */ /* diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index bae8ae6d91..b83681ad01 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -235,19 +235,18 @@ char_u *get_inserted(void) return get_buffcont(&redobuff, FALSE); } -/* - * Add string "s" after the current block of buffer "buf". - * K_SPECIAL and CSI should have been escaped already. - */ -static void -add_buff ( - buffheader_T *buf, - char_u *s, - ssize_t slen // length of "s" or -1 -) +/// Add string after the current block of the given buffer +/// +/// K_SPECIAL and CSI should have been escaped already. +/// +/// @param[out] buf Buffer to add to. +/// @param[in] s String to add. +/// @param[in] slen String length or -1 for NUL-terminated string. +static void add_buff(buffheader_T *const buf, const char *const s, + ptrdiff_t slen) { if (slen < 0) { - slen = (ssize_t)STRLEN(s); + slen = (ptrdiff_t)strlen(s); } if (slen == 0) { // don't add empty strings return; @@ -292,9 +291,8 @@ add_buff ( */ static void add_num_buff(buffheader_T *buf, long n) { - char_u number[32]; - - sprintf((char *)number, "%" PRId64, (int64_t)n); + char number[32]; + snprintf(number, sizeof(number), "%ld", n); add_buff(buf, number, -1L); } @@ -304,27 +302,29 @@ static void add_num_buff(buffheader_T *buf, long n) */ static void add_char_buff(buffheader_T *buf, int c) { - char_u bytes[MB_MAXBYTES + 1]; - int len; - int i; - char_u temp[4]; + char bytes[MB_MAXBYTES + 1]; - if (IS_SPECIAL(c)) + int len; + if (IS_SPECIAL(c)) { len = 1; - else - len = (*mb_char2bytes)(c, bytes); - for (i = 0; i < len; ++i) { - if (!IS_SPECIAL(c)) + } else { + len = (*mb_char2bytes)(c, (char_u *)bytes); + } + + for (int i = 0; i < len; i++) { + if (!IS_SPECIAL(c)) { c = bytes[i]; + } + char temp[4]; if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) { - /* translate special key code into three byte sequence */ - temp[0] = K_SPECIAL; - temp[1] = (char_u)K_SECOND(c); - temp[2] = (char_u)K_THIRD(c); + // Translate special key code into three byte sequence. + temp[0] = (char)K_SPECIAL; + temp[1] = (char)K_SECOND(c); + temp[2] = (char)K_THIRD(c); temp[3] = NUL; } else { - temp[0] = (char_u)c; + temp[0] = (char)c; temp[1] = NUL; } add_buff(buf, temp, -1L); @@ -479,16 +479,14 @@ static int save_level = 0; void saveRedobuff(void) { - char_u *s; - if (save_level++ == 0) { save_redobuff = redobuff; redobuff.bh_first.b_next = NULL; save_old_redobuff = old_redobuff; old_redobuff.bh_first.b_next = NULL; - /* Make a copy, so that ":normal ." in a function works. */ - s = get_buffcont(&save_redobuff, FALSE); + // Make a copy, so that ":normal ." in a function works. + char *const s = (char *)get_buffcont(&save_redobuff, false); if (s != NULL) { add_buff(&redobuff, s, -1L); xfree(s); @@ -514,10 +512,11 @@ void restoreRedobuff(void) * Append "s" to the redo buffer. * K_SPECIAL and CSI should already have been escaped. */ -void AppendToRedobuff(char_u *s) +void AppendToRedobuff(const char *s) { - if (!block_redo) - add_buff(&redobuff, s, -1L); + if (!block_redo) { + add_buff(&redobuff, (const char *)s, -1L); + } } /* @@ -530,44 +529,47 @@ AppendToRedobuffLit ( int len /* length of "str" or -1 for up to the NUL */ ) { - char_u *s = str; - int c; - char_u *start; - - if (block_redo) + if (block_redo) { return; + } + + const char *s = (const char *)str; + while (len < 0 ? *s != NUL : s - (const char *)str < len) { + // Put a string of normal characters in the redo buffer (that's + // faster). + const char *start = s; + while (*s >= ' ' && *s < DEL && (len < 0 || s - (const char *)str < len)) { + s++; + } - while (len < 0 ? *s != NUL : s - str < len) { - /* Put a string of normal characters in the redo buffer (that's - * faster). */ - start = s; - while (*s >= ' ' && *s < DEL && (len < 0 || s - str < len)) - ++s; - - /* Don't put '0' or '^' as last character, just in case a CTRL-D is - * typed next. */ - if (*s == NUL && (s[-1] == '0' || s[-1] == '^')) - --s; - if (s > start) + // Don't put '0' or '^' as last character, just in case a CTRL-D is + // typed next. + if (*s == NUL && (s[-1] == '0' || s[-1] == '^')) { + s--; + } + if (s > start) { add_buff(&redobuff, start, (long)(s - start)); + } - if (*s == NUL || (len >= 0 && s - str >= len)) + if (*s == NUL || (len >= 0 && s - (const char *)str >= len)) { break; + } - /* Handle a special or multibyte character. */ - if (has_mbyte) - /* Handle composing chars separately. */ - c = mb_cptr2char_adv(&s); - else - c = *s++; - if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) + // Handle a special or multibyte character. + // Composing chars separately are handled separately. + const int c = (has_mbyte + ? mb_cptr2char_adv((const char_u **)&s) + : (uint8_t)(*s++)); + if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) { add_char_buff(&redobuff, Ctrl_V); + } - /* CTRL-V '0' must be inserted as CTRL-V 048 */ - if (*s == NUL && c == '0') - add_buff(&redobuff, (char_u *)"048", 3L); - else + // CTRL-V '0' must be inserted as CTRL-V 048. + if (*s == NUL && c == '0') { + add_buff(&redobuff, "048", 3L); + } else { add_char_buff(&redobuff, c); + } } } @@ -594,19 +596,19 @@ void AppendNumberToRedobuff(long n) * Append string "s" to the stuff buffer. * CSI and K_SPECIAL must already have been escaped. */ -void stuffReadbuff(char_u *s) +void stuffReadbuff(const char *s) { add_buff(&readbuf1, s, -1L); } /// Append string "s" to the redo stuff buffer. /// @remark CSI and K_SPECIAL must already have been escaped. -void stuffRedoReadbuff(char_u *s) +void stuffRedoReadbuff(const char *s) { add_buff(&readbuf2, s, -1L); } -void stuffReadbuffLen(char_u *s, long len) +void stuffReadbuffLen(const char *s, long len) { add_buff(&readbuf1, s, len); } @@ -616,19 +618,18 @@ void stuffReadbuffLen(char_u *s, long len) * escaping other K_SPECIAL and CSI bytes. * Change CR, LF and ESC into a space. */ -void stuffReadbuffSpec(char_u *s) +void stuffReadbuffSpec(const char *s) { - int c; - while (*s != NUL) { - if (*s == K_SPECIAL && s[1] != NUL && s[2] != NUL) { - /* Insert special key literally. */ - stuffReadbuffLen(s, 3L); + if ((uint8_t)(*s) == K_SPECIAL && s[1] != NUL && s[2] != NUL) { + // Insert special key literally. + stuffReadbuffLen(s, 3); s += 3; } else { - c = mb_ptr2char_adv(&s); - if (c == CAR || c == NL || c == ESC) + int c = mb_ptr2char_adv((const char_u **)&s); + if (c == CAR || c == NL || c == ESC) { c = ' '; + } stuffcharReadbuff(c); } } @@ -747,8 +748,8 @@ int start_redo(long count, int old_redo) /* copy the buffer name, if present */ if (c == '"') { - add_buff(&readbuf2, (char_u *)"\"", 1L); - c = read_redo(FALSE, old_redo); + add_buff(&readbuf2, "\"", 1L); + c = read_redo(false, old_redo); /* if a numbered buffer is used, increment the number */ if (c >= '1' && c < '9') @@ -1091,21 +1092,19 @@ static void gotchars(char_u *chars, size_t len) { char_u *s = chars; int c; - char_u buf[2]; // remember how many chars were last recorded if (Recording) { last_recorded_len += len; } - buf[1] = NUL; while (len--) { // Handle one byte at a time; no translation to be done. c = *s++; updatescript(c); if (Recording) { - buf[0] = (char_u)c; + char buf[2] = { (char)c, NUL }; add_buff(&recordbuff, buf, 1L); } } @@ -1389,27 +1388,20 @@ int vgetc(void) for (;; ) { // this is done twice if there are modifiers bool did_inc = false; if (mod_mask) { // no mapping after modifier has been read - ++no_mapping; - ++allow_keys; + no_mapping++; did_inc = true; // mod_mask may change value } c = vgetorpeek(true); if (did_inc) { - --no_mapping; - --allow_keys; + no_mapping--; } - /* Get two extra bytes for special keys */ - if (c == K_SPECIAL - ) { - int save_allow_keys = allow_keys; - - ++no_mapping; - allow_keys = 0; /* make sure BS is not found */ - c2 = vgetorpeek(TRUE); /* no mapping for these chars */ - c = vgetorpeek(TRUE); - --no_mapping; - allow_keys = save_allow_keys; + // Get two extra bytes for special keys + if (c == K_SPECIAL) { + no_mapping++; + c2 = vgetorpeek(true); // no mapping for these chars + c = vgetorpeek(true); + no_mapping--; if (c2 == KS_MODIFIER) { mod_mask = c; continue; @@ -1487,7 +1479,7 @@ int vgetc(void) buf[i] = CSI; } } - --no_mapping; + no_mapping--; c = (*mb_ptr2char)(buf); } @@ -1570,7 +1562,7 @@ int char_avail(void) no_mapping++; retval = vpeekc(); - --no_mapping; + no_mapping--; return retval != NUL; } @@ -1888,9 +1880,8 @@ static int vgetorpeek(int advance) (size_t)(mlen - typebuf.tb_maplen)); } - del_typebuf(mlen, 0); /* remove the chars */ - set_option_value((char_u *)"paste", - (long)!p_paste, NULL, 0); + del_typebuf(mlen, 0); // Remove the chars. + set_option_value("paste", !p_paste, NULL, 0); if (!(State & INSERT)) { msg_col = 0; msg_row = (int)Rows - 1; @@ -1912,7 +1903,7 @@ static int vgetorpeek(int advance) } if ((mp == NULL || max_mlen >= mp_match_len) - && keylen != KEYLEN_PART_MAP) { + && keylen != KEYLEN_PART_MAP && keylen != KEYLEN_PART_KEY) { // No matching mapping found or found a non-matching mapping that // matches at least what the matching mapping matched keylen = 0; @@ -3226,82 +3217,99 @@ showmap ( ui_flush(); /* show one line at a time */ } -/* - * Return TRUE if a map exists that has "str" in the rhs for mode "modechars". - * Recognize termcap codes in "str". - * Also checks mappings local to the current buffer. - */ -int map_to_exists(char_u *str, char_u *modechars, int abbr) +/// Check if a map exists that has given string in the rhs +/// +/// Also checks mappings local to the current buffer. +/// +/// @param[in] str String which mapping must have in the rhs. Termcap codes +/// are recognized in this argument. +/// @param[in] modechars Mode(s) in which mappings are checked. +/// @param[in] abbr true if checking abbreviations in place of mappings. +/// +/// @return true if there is at least one mapping with given parameters. +bool map_to_exists(const char *const str, const char *const modechars, + const bool abbr) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { int mode = 0; - char_u *rhs; - char_u *buf; int retval; - rhs = replace_termcodes(str, STRLEN(str), &buf, false, true, false, - CPO_TO_CPO_FLAGS); - - if (vim_strchr(modechars, 'n') != NULL) - mode |= NORMAL; - if (vim_strchr(modechars, 'v') != NULL) - mode |= VISUAL + SELECTMODE; - if (vim_strchr(modechars, 'x') != NULL) - mode |= VISUAL; - if (vim_strchr(modechars, 's') != NULL) - mode |= SELECTMODE; - if (vim_strchr(modechars, 'o') != NULL) - mode |= OP_PENDING; - if (vim_strchr(modechars, 'i') != NULL) - mode |= INSERT; - if (vim_strchr(modechars, 'l') != NULL) - mode |= LANGMAP; - if (vim_strchr(modechars, 'c') != NULL) - mode |= CMDLINE; - - retval = map_to_exists_mode(rhs, mode, abbr); + char_u *buf; + char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf, + false, true, false, + CPO_TO_CPO_FLAGS); + +#define MAPMODE(mode, modechars, chr, modeflags) \ + do { \ + if (strchr(modechars, chr) != NULL) { \ + mode |= modeflags; \ + } \ + } while (0) + MAPMODE(mode, modechars, 'n', NORMAL); + MAPMODE(mode, modechars, 'v', VISUAL|SELECTMODE); + MAPMODE(mode, modechars, 'x', VISUAL); + MAPMODE(mode, modechars, 's', SELECTMODE); + MAPMODE(mode, modechars, 'o', OP_PENDING); + MAPMODE(mode, modechars, 'i', INSERT); + MAPMODE(mode, modechars, 'l', LANGMAP); + MAPMODE(mode, modechars, 'c', CMDLINE); +#undef MAPMODE + + retval = map_to_exists_mode((const char *)rhs, mode, abbr); xfree(buf); return retval; } -/* - * Return TRUE if a map exists that has "str" in the rhs for mode "mode". - * Also checks mappings local to the current buffer. - */ -int map_to_exists_mode(char_u *rhs, int mode, int abbr) +/// Check if a map exists that has given string in the rhs +/// +/// Also checks mappings local to the current buffer. +/// +/// @param[in] rhs String which mapping must have in the rhs. Termcap codes +/// are recognized in this argument. +/// @param[in] mode Mode(s) in which mappings are checked. +/// @param[in] abbr true if checking abbreviations in place of mappings. +/// +/// @return true if there is at least one mapping with given parameters. +int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr) { mapblock_T *mp; int hash; - int expand_buffer = FALSE; + bool expand_buffer = false; validate_maphash(); - /* Do it twice: once for global maps and once for local maps. */ - for (;; ) { - for (hash = 0; hash < 256; ++hash) { + // Do it twice: once for global maps and once for local maps. + for (;;) { + for (hash = 0; hash < 256; hash++) { if (abbr) { - if (hash > 0) /* there is only one abbr list */ + if (hash > 0) { // There is only one abbr list. break; - if (expand_buffer) + } + if (expand_buffer) { mp = curbuf->b_first_abbr; - else + } else { mp = first_abbr; - } else if (expand_buffer) + } + } else if (expand_buffer) { mp = curbuf->b_maphash[hash]; - else + } else { mp = maphash[hash]; + } for (; mp; mp = mp->m_next) { if ((mp->m_mode & mode) - && strstr((char *)mp->m_str, (char *)rhs) != NULL) - return TRUE; + && strstr((char *)mp->m_str, rhs) != NULL) { + return true; + } } } - if (expand_buffer) + if (expand_buffer) { break; - expand_buffer = TRUE; + } + expand_buffer = true; } - return FALSE; + return false; } /* diff --git a/src/nvim/gettext.h b/src/nvim/gettext.h new file mode 100644 index 0000000000..aa0e97233e --- /dev/null +++ b/src/nvim/gettext.h @@ -0,0 +1,21 @@ +#ifndef NVIM_GETTEXT_H +#define NVIM_GETTEXT_H + +#ifdef HAVE_WORKING_LIBINTL +# include <libintl.h> +# define _(x) gettext((char *)(x)) +// XXX do we actually need this? +# ifdef gettext_noop +# define N_(x) gettext_noop(x) +# else +# define N_(x) x +# endif +#else +# define _(x) ((char *)(x)) +# define N_(x) x +# define bindtextdomain(x, y) // empty +# define bind_textdomain_codeset(x, y) // empty +# define textdomain(x) // empty +#endif + +#endif // NVIM_GETTEXT_H diff --git a/src/nvim/globals.h b/src/nvim/globals.h index f8c7c9d330..3c705d88a5 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -12,6 +12,7 @@ #include "nvim/syntax_defs.h" #include "nvim/types.h" #include "nvim/event/loop.h" +#include "nvim/os/os_defs.h" #define IOSIZE (1024+1) // file I/O and sprintf buffer size @@ -21,16 +22,6 @@ # define MSG_BUF_CLEN (MSG_BUF_LEN / 6) // cell length (worst case: utf-8 // takes 6 bytes for one cell) -// Maximum length of a file path. Make it a bit long, to stay -// on the safe side. But not too long to put on the stack. -#ifndef MAXPATHL -# ifdef MAXPATHLEN -# define MAXPATHL MAXPATHLEN -# else -# define MAXPATHL 256 -# endif -#endif - #ifdef WIN32 # define _PATHSEPSTR "\\" #else @@ -473,6 +464,7 @@ typedef enum { , HLF_CUL // 'cursurline' , HLF_MC // 'colorcolumn' , HLF_QFL // selected quickfix line + , HLF_0 // Whitespace , HLF_COUNT // MUST be the last one } hlf_T; @@ -481,7 +473,7 @@ typedef enum { #define HL_FLAGS { '8', '~', 'z', 'Z', '@', 'd', 'e', 'i', 'l', 'm', 'M', 'n', \ 'N', 'r', 's', 'S', 'c', 't', 'v', 'V', 'w', 'W', 'f', 'F', \ 'A', 'C', 'D', 'T', '-', '>', 'B', 'P', 'R', 'L', '+', '=', \ - 'x', 'X', '*', '#', '_', '!', '.', 'o', 'q' } + 'x', 'X', '*', '#', '_', '!', '.', 'o', 'q', '0' } EXTERN int highlight_attr[HLF_COUNT]; /* Highl. attr for each context. */ EXTERN int highlight_user[9]; /* User[1-9] attributes */ @@ -832,13 +824,11 @@ EXTERN int ex_no_reprint INIT(= FALSE); /* no need to print after z or p */ EXTERN int Recording INIT(= FALSE); /* TRUE when recording into a reg. */ EXTERN int Exec_reg INIT(= FALSE); /* TRUE when executing a register */ -EXTERN int no_mapping INIT(= FALSE); /* currently no mapping allowed */ -EXTERN int no_zero_mapping INIT(= 0); /* mapping zero not allowed */ -EXTERN int allow_keys INIT(= FALSE); /* allow key codes when no_mapping - * is set */ -EXTERN int no_u_sync INIT(= 0); /* Don't call u_sync() */ -EXTERN int u_sync_once INIT(= 0); /* Call u_sync() once when evaluating - an expression. */ +EXTERN int no_mapping INIT(= false); // currently no mapping allowed +EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed +EXTERN int no_u_sync INIT(= 0); // Don't call u_sync() +EXTERN int u_sync_once INIT(= 0); // Call u_sync() once when evaluating + // an expression. EXTERN bool force_restart_edit INIT(= false); // force restart_edit after // ex_normal returns @@ -1210,6 +1200,7 @@ EXTERN char_u e_dirnotf[] INIT(= N_( "E919: Directory not found in '%s': \"%s\"")); EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported")); EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long")); +EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String")); EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); @@ -1228,11 +1219,6 @@ EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */ EXTERN int ignored; EXTERN char *ignoredp; -EXTERN bool in_free_unref_items INIT(= false); - -// Used for checking if local variables or arguments used in a lambda. -EXTERN int *eval_lavars_used INIT(= NULL); - // If a msgpack-rpc channel should be started over stdin/stdout EXTERN bool embedded_mode INIT(= false); @@ -1257,7 +1243,7 @@ typedef enum { kCdScopeInvalid = -1, kCdScopeWindow, ///< Affects one window. kCdScopeTab, ///< Affects one tab page. - kCdScopeGlobal, ///< Affects the entire instance of Neovim. + kCdScopeGlobal, ///< Affects the entire Nvim instance. } CdScope; #define MIN_CD_SCOPE kCdScopeWindow diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 19d97ecfef..4cb05ffc12 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -369,7 +369,6 @@ static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) { int colorindex; uint32_t fg_color; - char *color; pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL); pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL); @@ -377,11 +376,12 @@ static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL); { - color = (char *)highlight_color(hl_id, (char_u *)"fg", modec); - if (color == NULL) + const char *color = highlight_color(hl_id, "fg", modec); + if (color == NULL) { colorindex = 0; - else + } else { colorindex = atoi(color); + } if (colorindex >= 0 && colorindex < t_colors) fg_color = prt_get_term_color(colorindex); diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c index b14bb01c0e..354c9718ba 100644 --- a/src/nvim/hashtab.c +++ b/src/nvim/hashtab.c @@ -82,7 +82,7 @@ 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(hashtab_T *ht, const char_u *key) +hashitem_T *hash_find(const hashtab_T *const ht, const char_u *const key) { return hash_lookup(ht, (const char *)key, STRLEN(key), hash_hash(key)); } @@ -99,7 +99,8 @@ hashitem_T *hash_find(hashtab_T *ht, const char_u *key) /// /// @warning Returned pointer becomes invalid as soon as the hash table /// is changed in any way. -hashitem_T *hash_find_len(hashtab_T *ht, const char *key, const size_t len) +hashitem_T *hash_find_len(const hashtab_T *const ht, const char *const key, + const size_t len) { return hash_lookup(ht, key, len, hash_hash_len(key, len)); } @@ -115,7 +116,7 @@ hashitem_T *hash_find_len(hashtab_T *ht, const char *key, const size_t len) /// used for that key. /// WARNING: Returned pointer becomes invalid as soon as the hash table /// is changed in any way. -hashitem_T *hash_lookup(hashtab_T *const ht, +hashitem_T *hash_lookup(const hashtab_T *const ht, const char *const key, const size_t key_len, const hash_T hash) { diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h index 0da2b13f2e..973b97d476 100644 --- a/src/nvim/hashtab.h +++ b/src/nvim/hashtab.h @@ -70,6 +70,25 @@ typedef struct hashtable_S { hashitem_T ht_smallarray[HT_INIT_SIZE]; /// initial array } hashtab_T; +/// Iterate over a hashtab +/// +/// @param[in] ht Hashtab to iterate over. +/// @param hi Name of the variable with current hashtab entry. +/// @param code Cycle body. +#define HASHTAB_ITER(ht, hi, code) \ + do { \ + hashtab_T *const hi##ht_ = (ht); \ + size_t hi##todo_ = hi##ht_->ht_used; \ + for (hashitem_T *hi = hi##ht_->ht_array; hi##todo_; hi++) { \ + if (!HASHITEM_EMPTY(hi)) { \ + { \ + code \ + } \ + hi##todo_--; \ + } \ + } \ + } while (0) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "hashtab.h.generated.h" #endif diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 550d256de5..b647b8146a 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -140,31 +140,30 @@ char_u *get_cscope_name(expand_T *xp, int idx) /* * Handle command line completion for :cscope command. */ -void set_context_in_cscope_cmd(expand_T *xp, char_u *arg, cmdidx_T cmdidx) +void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx) { - char_u *p; - - /* Default: expand subcommands */ + // Default: expand subcommands. xp->xp_context = EXPAND_CSCOPE; - xp->xp_pattern = arg; - expand_what = (cmdidx == CMD_scscope) - ? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD; + xp->xp_pattern = (char_u *)arg; + expand_what = ((cmdidx == CMD_scscope) + ? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD); /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past first word */ - xp->xp_pattern = skipwhite(p); - if (*skiptowhite(xp->xp_pattern) != NUL) + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past first word. + xp->xp_pattern = skipwhite((const char_u *)p); + if (*skiptowhite(xp->xp_pattern) != NUL) { xp->xp_context = EXPAND_NOTHING; - else if (STRNICMP(arg, "add", p - arg) == 0) + } else if (STRNICMP(arg, "add", p - arg) == 0) { xp->xp_context = EXPAND_FILES; - else if (STRNICMP(arg, "kill", p - arg) == 0) + } else if (STRNICMP(arg, "kill", p - arg) == 0) { expand_what = EXP_CSCOPE_KILL; - else if (STRNICMP(arg, "find", p - arg) == 0) + } else if (STRNICMP(arg, "find", p - arg) == 0) { expand_what = EXP_CSCOPE_FIND; - else + } else { xp->xp_context = EXPAND_NOTHING; + } } } } diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 7b758b4dac..4a73fbaf61 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -174,7 +174,7 @@ static char_u *skip_string(char_u *p) char_u *paren = vim_strchr(delim, '('); if (paren != NULL) { - ptrdiff_t delim_len = paren - delim; + const ptrdiff_t delim_len = paren - delim; for (p += 3; *p; ++p) if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0 diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 650bf76156..a8df6322cf 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -19,6 +19,15 @@ # define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif +/// String with length +/// +/// For use in functions which accept (char *s, size_t len) pair in arguments. +/// +/// @param[in] s Static string. +/// +/// @return `s, sizeof(s) - 1` +#define S_LEN(s) (s), (sizeof(s) - 1) + /* * Position comparisons */ @@ -85,7 +94,7 @@ do { \ if (*p_langmap \ && (condition) \ - && (!p_lnr || (p_lnr && typebuf_maplen() == 0)) \ + && (p_lrm || (!p_lrm && KeyTyped)) \ && !KeyStuffed \ && (c) >= 0) \ { \ diff --git a/src/nvim/main.c b/src/nvim/main.c index 4416bd067c..7ad42d6776 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -283,7 +283,7 @@ int main(int argc, char **argv) cmdline_row = (int)(Rows - p_ch); msg_row = cmdline_row; screenalloc(false); /* allocate screen buffers */ - set_init_2(); + set_init_2(params.headless); TIME_MSG("inits 2"); msg_scroll = TRUE; @@ -326,6 +326,12 @@ int main(int argc, char **argv) do_cmdline_cmd("augroup END"); #undef PROTO + // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments. + // Allows for setting 'loadplugins' there. + if (params.use_vimrc != NULL && strcmp(params.use_vimrc, "NONE") == 0) { + p_lpl = false; + } + /* Execute --cmd arguments. */ exe_pre_commands(¶ms); @@ -385,9 +391,10 @@ int main(int argc, char **argv) shada_read_everything(NULL, false, true); TIME_MSG("reading ShaDa"); } - /* It's better to make v:oldfiles an empty list than NULL. */ - if (get_vim_var_list(VV_OLDFILES) == NULL) - set_vim_var_list(VV_OLDFILES, list_alloc()); + // It's better to make v:oldfiles an empty list than NULL. + if (get_vim_var_list(VV_OLDFILES) == NULL) { + set_vim_var_list(VV_OLDFILES, tv_list_alloc()); + } /* * "-q errorfile": Load the error file now. @@ -796,17 +803,18 @@ static void command_line_scan(mparm_T *parmp) argv_idx = -1; /* skip to next argument */ break; - case 'A': /* "-A" start in Arabic mode */ - set_option_value((char_u *)"arabic", 1L, NULL, 0); + case 'A': { // "-A" start in Arabic mode. + set_option_value("arabic", 1L, NULL, 0); break; - - case 'b': /* "-b" binary mode */ - /* Needs to be effective before expanding file names, because - * for Win32 this makes us edit a shortcut file itself, - * instead of the file it links to. */ + } + case 'b': { // "-b" binary mode. + // Needs to be effective before expanding file names, because + // for Win32 this makes us edit a shortcut file itself, + // instead of the file it links to. set_options_bin(curbuf->b_p_bin, 1, 0); - curbuf->b_p_bin = 1; /* binary file I/O */ + curbuf->b_p_bin = 1; // Binary file I/O. break; + } case 'e': /* "-e" Ex mode */ exmode_active = EXMODE_NORMAL; @@ -823,24 +831,27 @@ static void command_line_scan(mparm_T *parmp) main_start_gui(); break; - case 'F': /* "-F" start in Farsi mode: rl + fkmap set */ - p_fkmap = TRUE; - set_option_value((char_u *)"rl", 1L, NULL, 0); + case 'F': { // "-F" start in Farsi mode: rl + fkmap set. + p_fkmap = true; + set_option_value("rl", 1L, NULL, 0); break; + } case 'h': /* "-h" give help message */ usage(); mch_exit(0); - case 'H': /* "-H" start in Hebrew mode: rl + hkmap set */ - p_hkmap = TRUE; - set_option_value((char_u *)"rl", 1L, NULL, 0); + case 'H': { // "-H" start in Hebrew mode: rl + hkmap set. + p_hkmap = true; + set_option_value("rl", 1L, NULL, 0); break; + } - case 'l': /* "-l" lisp mode, 'lisp' and 'showmatch' on */ - set_option_value((char_u *)"lisp", 1L, NULL, 0); - p_sm = TRUE; + case 'l': { // "-l" lisp mode, 'lisp' and 'showmatch' on. + set_option_value("lisp", 1L, NULL, 0); + p_sm = true; break; + } case 'M': /* "-M" no changes or writing of files */ reset_modifiable(); @@ -939,8 +950,7 @@ static void command_line_scan(mparm_T *parmp) /* default is 10: a little bit verbose */ p_verbose = get_number_arg(argv[0], &argv_idx, 10); if (argv[0][argv_idx] != NUL) { - set_option_value((char_u *)"verbosefile", 0L, - (char_u *)argv[0] + argv_idx, 0); + set_option_value("verbosefile", 0L, argv[0] + argv_idx, 0); argv_idx = (int)STRLEN(argv[0]); } break; @@ -949,7 +959,7 @@ static void command_line_scan(mparm_T *parmp) /* "-w {scriptout}" write to script */ if (ascii_isdigit(((char_u *)argv[0])[argv_idx])) { n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value((char_u *)"window", n, NULL, 0); + set_option_value("window", n, NULL, 0); break; } want_argument = TRUE; @@ -1081,7 +1091,7 @@ scripterror: if (ascii_isdigit(*((char_u *)argv[0]))) { argv_idx = 0; n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value((char_u *)"window", n, NULL, 0); + set_option_value("window", n, NULL, 0); argv_idx = -1; break; } @@ -1268,11 +1278,14 @@ static void set_window_layout(mparm_T *paramp) static void load_plugins(void) { if (p_lpl) { - source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL); // NOLINT + source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_NOAFTER); // NOLINT TIME_MSG("loading plugins"); ex_packloadall(NULL); TIME_MSG("loading packages"); + + source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_AFTER); + TIME_MSG("loading after plugins"); } } @@ -1708,8 +1721,6 @@ static void source_startup_scripts(const mparm_T *const parmp) if (parmp->use_vimrc != NULL) { if (strcmp(parmp->use_vimrc, "NONE") == 0 || strcmp(parmp->use_vimrc, "NORC") == 0) { - if (parmp->use_vimrc[2] == 'N') - p_lpl = false; // don't load plugins either } else { if (do_source((char_u *)parmp->use_vimrc, FALSE, DOSO_NONE) != OK) EMSG2(_("E282: Cannot read from \"%s\""), parmp->use_vimrc); diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 4e05845eb5..ae94ec7ecd 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -62,7 +62,7 @@ int setmark(int c) /// Free fmark_T item void free_fmark(fmark_T fm) { - dict_unref(fm.additional_data); + tv_dict_unref(fm.additional_data); } /// Free xfmark_T item @@ -887,6 +887,23 @@ void ex_changes(exarg_T *eap) */ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) { + mark_adjust_internal(line1, line2, amount, amount_after, true); +} + +// mark_adjust_nofold() does the same as mark_adjust() but without adjusting +// folds in any way. Folds must be adjusted manually by the caller. +// This is only useful when folds need to be moved in a way different to +// calling foldMarkAdjust() with arguments line1, line2, amount, amount_after, +// for an example of why this may be necessary, see do_move(). +void mark_adjust_nofold(linenr_T line1, linenr_T line2, long amount, + long amount_after) +{ + mark_adjust_internal(line1, line2, amount, amount_after, false); +} + +static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, + long amount_after, bool adjust_folds) +{ int i; int fnum = curbuf->b_fnum; linenr_T *lp; @@ -1011,8 +1028,9 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) } } - /* adjust folds */ - foldMarkAdjust(win, line1, line2, amount, amount_after); + if (adjust_folds) { + foldMarkAdjust(win, line1, line2, amount, amount_after); + } } } @@ -1413,3 +1431,26 @@ void free_all_marks(void) memset(&namedfm[0], 0, sizeof(namedfm)); } #endif + +/// Adjust position to point to the first byte of a multi-byte character +/// +/// If it points to a tail byte it is move backwards to the head byte. +/// +/// @param[in] buf Buffer to adjust position in. +/// @param[out] lp Position to adjust. +void mark_mb_adjustpos(buf_T *buf, pos_T *lp) + FUNC_ATTR_NONNULL_ALL +{ + if (lp->col > 0 || lp->coladd > 1) { + const char_u *const p = ml_get_buf(buf, lp->lnum, false); + lp->col -= (*mb_head_off)(p, p + lp->col); + // Reset "coladd" when the cursor would be on the right half of a + // double-wide character. + if (lp->coladd == 1 + && p[lp->col] != TAB + && vim_isprintc((*mb_ptr2char)(p + lp->col)) + && ptr2cells(p + lp->col) > 1) { + lp->coladd = 0; + } + } +} diff --git a/src/nvim/mark.h b/src/nvim/mark.h index aff6e7273a..efba9708db 100644 --- a/src/nvim/mark.h +++ b/src/nvim/mark.h @@ -29,7 +29,7 @@ /// 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) /// Set given extended mark (regular mark + file name) #define SET_XFMARK(xfmarkp_, mark_, fnum_, fname_) \ diff --git a/src/nvim/mark_defs.h b/src/nvim/mark_defs.h index 720b2475ed..2cb489501e 100644 --- a/src/nvim/mark_defs.h +++ b/src/nvim/mark_defs.h @@ -3,7 +3,7 @@ #include "nvim/pos.h" #include "nvim/os/time.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" /* * marks: positions in a file diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 6a87a63b8c..d96848754c 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -50,6 +50,7 @@ #include "nvim/strings.h" #include "nvim/os/os.h" #include "nvim/arabic.h" +#include "nvim/mark.h" typedef struct { int rangeStart; @@ -355,14 +356,13 @@ int bomb_size(void) */ void remove_bom(char_u *s) { - if (enc_utf8) { - char_u *p = s; + char_u *p = s; - while ((p = vim_strbyte(p, 0xef)) != NULL) { - if (p[1] == 0xbb && p[2] == 0xbf) - STRMOVE(p, p + 3); - else - ++p; + while ((p = vim_strbyte(p, 0xef)) != NULL) { + if (p[1] == 0xbb && p[2] == 0xbf) { + STRMOVE(p, p + 3); + } else { + p++; } } } @@ -376,166 +376,21 @@ void remove_bom(char_u *s) */ int mb_get_class(const char_u *p) { - return mb_get_class_buf(p, curbuf); + return mb_get_class_tab(p, curbuf->b_chartab); } -int mb_get_class_buf(const char_u *p, buf_T *buf) +int mb_get_class_tab(const char_u *p, const uint64_t *const chartab) { if (MB_BYTE2LEN(p[0]) == 1) { - if (p[0] == NUL || ascii_iswhite(p[0])) + if (p[0] == NUL || ascii_iswhite(p[0])) { return 0; - if (vim_iswordc_buf(p[0], buf)) + } + if (vim_iswordc_tab(p[0], chartab)) { return 2; + } return 1; } - if (enc_dbcs != 0 && p[0] != NUL && p[1] != NUL) - return dbcs_class(p[0], p[1]); - if (enc_utf8) - return utf_class(utf_ptr2char(p)); - return 0; -} - -/* - * Get class of a double-byte character. This always returns 3 or bigger. - * TODO: Should return 1 for punctuation. - */ -int dbcs_class(unsigned lead, unsigned trail) -{ - switch (enc_dbcs) { - /* please add classify routine for your language in here */ - - case DBCS_JPNU: /* ? */ - case DBCS_JPN: - { - /* JIS code classification */ - unsigned char lb = lead; - unsigned char tb = trail; - - /* convert process code to JIS */ - /* - * XXX: Code page identification can not use with all - * system! So, some other encoding information - * will be needed. - * In japanese: SJIS,EUC,UNICODE,(JIS) - * Note that JIS-code system don't use as - * process code in most system because it uses - * escape sequences(JIS is context depend encoding). - */ - /* assume process code is JAPANESE-EUC */ - lb &= 0x7f; - tb &= 0x7f; - /* exceptions */ - switch (lb << 8 | tb) { - case 0x2121: /* ZENKAKU space */ - return 0; - case 0x2122: /* TOU-TEN (Japanese comma) */ - case 0x2123: /* KU-TEN (Japanese period) */ - case 0x2124: /* ZENKAKU comma */ - case 0x2125: /* ZENKAKU period */ - return 1; - case 0x213c: /* prolongedsound handled as KATAKANA */ - return 13; - } - /* sieved by KU code */ - switch (lb) { - case 0x21: - case 0x22: - /* special symbols */ - return 10; - case 0x23: - /* alpha-numeric */ - return 11; - case 0x24: - /* hiragana */ - return 12; - case 0x25: - /* katakana */ - return 13; - case 0x26: - /* greek */ - return 14; - case 0x27: - /* russian */ - return 15; - case 0x28: - /* lines */ - return 16; - default: - /* kanji */ - return 17; - } - } - - case DBCS_KORU: /* ? */ - case DBCS_KOR: - { - /* KS code classification */ - unsigned char c1 = lead; - unsigned char c2 = trail; - - /* - * 20 : Hangul - * 21 : Hanja - * 22 : Symbols - * 23 : Alpha-numeric/Roman Letter (Full width) - * 24 : Hangul Letter(Alphabet) - * 25 : Roman Numeral/Greek Letter - * 26 : Box Drawings - * 27 : Unit Symbols - * 28 : Circled/Parenthesized Letter - * 29 : Hiragana/Katakana - * 30 : Cyrillic Letter - */ - - if (c1 >= 0xB0 && c1 <= 0xC8) - /* Hangul */ - return 20; - - else if (c1 >= 0xCA && c1 <= 0xFD) - /* Hanja */ - return 21; - else switch (c1) { - case 0xA1: - case 0xA2: - /* Symbols */ - return 22; - case 0xA3: - /* Alpha-numeric */ - return 23; - case 0xA4: - /* Hangul Letter(Alphabet) */ - return 24; - case 0xA5: - /* Roman Numeral/Greek Letter */ - return 25; - case 0xA6: - /* Box Drawings */ - return 26; - case 0xA7: - /* Unit Symbols */ - return 27; - case 0xA8: - case 0xA9: - if (c2 <= 0xAF) - return 25; /* Roman Letter */ - else if (c2 >= 0xF6) - return 22; /* Symbols */ - else - /* Circled/Parenthesized Letter */ - return 28; - case 0xAA: - case 0xAB: - /* Hiragana/Katakana */ - return 29; - case 0xAC: - /* Cyrillic Letter */ - return 30; - } - } - default: - break; - } - return 3; + return utf_class(utf_ptr2char(p)); } /* @@ -728,7 +583,7 @@ int utf_ptr2char(const char_u *p) * If byte sequence is illegal or incomplete, returns -1 and does not advance * "s". */ -static int utf_safe_read_char_adv(char_u **s, size_t *n) +static int utf_safe_read_char_adv(const char_u **s, size_t *n) { int c; @@ -770,7 +625,7 @@ static int utf_safe_read_char_adv(char_u **s, size_t *n) * Get character at **pp and advance *pp to the next character. * Note: composing characters are skipped! */ -int mb_ptr2char_adv(char_u **pp) +int mb_ptr2char_adv(const char_u **const pp) { int c; @@ -783,15 +638,12 @@ int mb_ptr2char_adv(char_u **pp) * Get character at **pp and advance *pp to the next character. * Note: composing characters are returned as separate characters. */ -int mb_cptr2char_adv(char_u **pp) +int mb_cptr2char_adv(const char_u **pp) { int c; c = (*mb_ptr2char)(*pp); - if (enc_utf8) - *pp += utf_ptr2len(*pp); - else - *pp += (*mb_ptr2len)(*pp); + *pp += utf_ptr2len(*pp); return c; } @@ -1381,7 +1233,8 @@ bool utf_isupper(int a) return utf_tolower(a) != a; } -static int utf_strnicmp(char_u *s1, char_u *s2, size_t n1, size_t n2) +static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, + size_t n2) { int c1, c2, cdiff; char_u buffer[6]; @@ -1540,48 +1393,26 @@ int utf16_to_utf8(const WCHAR *strw, char **str) * Returns zero if s1 and s2 are equal (ignoring case), the difference between * two characters otherwise. */ -int mb_strnicmp(char_u *s1, char_u *s2, size_t nn) -{ - int i, l; - int cdiff; - int n = (int)nn; - - if (enc_utf8) { - return utf_strnicmp(s1, s2, nn, nn); - } else { - for (i = 0; i < n; i += l) { - if (s1[i] == NUL && s2[i] == NUL) /* both strings end */ - return 0; - - l = (*mb_ptr2len)(s1 + i); - if (l <= 1) { - /* Single byte: first check normally, then with ignore case. */ - if (s1[i] != s2[i]) { - cdiff = vim_tolower(s1[i]) - vim_tolower(s2[i]); - if (cdiff != 0) - return cdiff; - } - } else { - /* For non-Unicode multi-byte don't ignore case. */ - if (l > n - i) - l = n - i; - cdiff = STRNCMP(s1 + i, s2 + i, l); - if (cdiff != 0) - return cdiff; - } - } - } - return 0; +int mb_strnicmp(const char_u *s1, const char_u *s2, const size_t nn) +{ + return utf_strnicmp(s1, s2, nn, nn); } -/* We need to call mb_stricmp() even when we aren't dealing with a multi-byte - * encoding because mb_stricmp() takes care of all ascii and non-ascii - * encodings, including characters with umlauts in latin1, etc., while - * STRICMP() only handles the system locale version, which often does not - * handle non-ascii properly. */ -int mb_stricmp(char_u *s1, char_u *s2) +/// Compare strings case-insensitively +/// +/// @note We need to call mb_stricmp() even when we aren't dealing with +/// a multi-byte encoding because mb_stricmp() takes care of all ASCII and +/// non-ascii encodings, including characters with umlauts in latin1, +/// etc., while STRICMP() only handles the system locale version, which +/// often does not handle non-ascii properly. +/// +/// @param[in] s1 First string to compare, not more then #MAXCOL characters. +/// @param[in] s2 Second string to compare, not more then #MAXCOL characters. +/// +/// @return 0 if strings are equal, <0 if s1 < s2, >0 if s1 > s2. +int mb_stricmp(const char *s1, const char *s2) { - return mb_strnicmp(s1, s2, MAXCOL); + return mb_strnicmp((const char_u *)s1, (const char_u *)s2, MAXCOL); } /* @@ -1699,27 +1530,24 @@ int mb_off_next(char_u *base, char_u *p) int i; int j; - if (enc_utf8) { - if (*p < 0x80) /* be quick for ASCII */ - return 0; + if (*p < 0x80) { // be quick for ASCII + return 0; + } - /* Find the next character that isn't 10xx.xxxx */ - for (i = 0; (p[i] & 0xc0) == 0x80; ++i) - ; - if (i > 0) { - /* Check for illegal sequence. */ - for (j = 0; p - j > base; ++j) - if ((p[-j] & 0xc0) != 0x80) - break; - if (utf8len_tab[p[-j]] != i + j) - return 0; + // Find the next character that isn't 10xx.xxxx + for (i = 0; (p[i] & 0xc0) == 0x80; i++) {} + if (i > 0) { + // Check for illegal sequence. + for (j = 0; p - j > base; j++) { + if ((p[-j] & 0xc0) != 0x80) { + break; + } + } + if (utf8len_tab[p[-j]] != i + j) { + return 0; } - return i; } - - /* Only need to check if we're on a trail byte, it doesn't matter if we - * want the offset to the next or current character. */ - return (*mb_head_off)(base, p); + return i; } /* @@ -1762,10 +1590,10 @@ void utf_find_illegal(void) char_u *tofree = NULL; vimconv.vc_type = CONV_NONE; - if (enc_utf8 && (enc_canon_props(curbuf->b_p_fenc) & ENC_8BIT)) { - /* 'encoding' is "utf-8" but we are editing a 8-bit encoded file, - * possibly a utf-8 file with illegal bytes. Setup for conversion - * from utf-8 to 'fileencoding'. */ + if (enc_canon_props(curbuf->b_p_fenc) & ENC_8BIT) { + // 'encoding' is "utf-8" but we are editing a 8-bit encoded file, + // possibly a utf-8 file with illegal bytes. Setup for conversion + // from utf-8 to 'fileencoding'. convert_setup(&vimconv, p_enc, curbuf->b_p_fenc); } @@ -1822,29 +1650,42 @@ theend: */ void mb_adjust_cursor(void) { - mb_adjustpos(curbuf, &curwin->w_cursor); + mark_mb_adjustpos(curbuf, &curwin->w_cursor); } -/* - * Adjust position "*lp" to point to the first byte of a multi-byte character. - * If it points to a tail byte it's moved backwards to the head byte. - */ -void mb_adjustpos(buf_T *buf, pos_T *lp) +/// Checks and adjusts cursor column. Not mode-dependent. +/// @see check_cursor_col_win +/// +/// @param win_ Places cursor on a valid column for this window. +void mb_check_adjust_col(void *win_) { - char_u *p; + win_T *win = (win_T *)win_; + colnr_T oldcol = win->w_cursor.col; + + // Column 0 is always valid. + if (oldcol != 0) { + char_u *p = ml_get_buf(win->w_buffer, win->w_cursor.lnum, false); + colnr_T len = (colnr_T)STRLEN(p); - if (lp->col > 0 - || lp->coladd > 1 - ) { - p = ml_get_buf(buf, lp->lnum, FALSE); - lp->col -= (*mb_head_off)(p, p + lp->col); - /* Reset "coladd" when the cursor would be on the right half of a - * double-wide character. */ - if (lp->coladd == 1 - && p[lp->col] != TAB - && vim_isprintc((*mb_ptr2char)(p + lp->col)) - && ptr2cells(p + lp->col) > 1) - lp->coladd = 0; + // Empty line or invalid column? + if (len == 0 || oldcol < 0) { + win->w_cursor.col = 0; + } else { + // Cursor column too big for line? + if (oldcol > len) { + win->w_cursor.col = len - 1; + } + // Move the cursor to the head byte. + win->w_cursor.col -= (*mb_head_off)(p, p + win->w_cursor.col); + } + + // Reset `coladd` when the cursor would be on the right half of a + // double-wide character. + if (win->w_cursor.coladd == 1 && p[win->w_cursor.col] != TAB + && vim_isprintc((*mb_ptr2char)(p + win->w_cursor.col)) + && ptr2cells(p + win->w_cursor.col) > 1) { + win->w_cursor.coladd = 0; + } } } @@ -2187,8 +2028,8 @@ void * my_iconv_open(char_u *to, char_u *from) * Returns the converted string in allocated memory. NULL for an error. * If resultlenp is not NULL, sets it to the result length in bytes. */ -static char_u * iconv_string(vimconv_T *vcp, char_u *str, size_t slen, - size_t *unconvlenp, size_t *resultlenp) +static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, + size_t slen, size_t *unconvlenp, size_t *resultlenp) { const char *from; size_t fromlen; @@ -2244,13 +2085,7 @@ static char_u * iconv_string(vimconv_T *vcp, char_u *str, size_t slen, *to++ = '?'; if ((*mb_ptr2cells)((char_u *)from) > 1) *to++ = '?'; - if (enc_utf8) - l = utfc_ptr2len_len((const char_u *)from, (int)fromlen); - else { - l = (*mb_ptr2len)((char_u *)from); - if (l > (int)fromlen) - l = (int)fromlen; - } + l = utfc_ptr2len_len((const char_u *)from, (int)fromlen); from += l; fromlen -= l; } else if (ICONV_ERRNO != ICONV_E2BIG) { @@ -2479,7 +2314,7 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, * Illegal chars are often changed to "?", unless vcp->vc_fail is set. * When something goes wrong, NULL is returned and "*lenp" is unchanged. */ -char_u * string_convert(vimconv_T *vcp, char_u *ptr, size_t *lenp) +char_u *string_convert(const vimconv_T *const vcp, char_u *ptr, size_t *lenp) { return string_convert_ext(vcp, ptr, lenp, NULL); } @@ -2489,7 +2324,7 @@ char_u * string_convert(vimconv_T *vcp, char_u *ptr, size_t *lenp) * an incomplete sequence at the end it is not converted and "*unconvlenp" is * set to the number of remaining bytes. */ -char_u * string_convert_ext(vimconv_T *vcp, char_u *ptr, +char_u * string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp, size_t *unconvlenp) { char_u *retval = NULL; diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 2c92a0fbb2..3565202466 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -2,6 +2,11 @@ #define NVIM_MBYTE_H #include <stdbool.h> +#include <string.h> + +#include "nvim/iconv.h" +#include "nvim/func_attr.h" +#include "nvim/os/os_defs.h" // For WCHAR, indirect /* * Return byte length of character that starts with byte "b". @@ -40,7 +45,41 @@ #define mb_ptr2char utf_ptr2char #define mb_head_off utf_head_off +/// Flags for vimconv_T +typedef enum { + CONV_NONE = 0, + CONV_TO_UTF8 = 1, + CONV_9_TO_UTF8 = 2, + CONV_TO_LATIN1 = 3, + CONV_TO_LATIN9 = 4, + CONV_ICONV = 5, +} ConvFlags; + +/// Structure used for string conversions +typedef struct { + int vc_type; ///< Zero or more ConvFlags. + int vc_factor; ///< Maximal expansion factor. +# ifdef USE_ICONV + iconv_t vc_fd; ///< Value for CONV_ICONV. +# endif + bool vc_fail; ///< What to do with invalid characters: if true, fail, + ///< otherwise use '?'. +} vimconv_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mbyte.h.generated.h" #endif + +static inline int mb_strcmp_ic(bool ic, const char *s1, const char *s2) + REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Compare strings +/// +/// @param[in] ic True if case is to be ignored. +/// +/// @return 0 if s1 == s2, <0 if s1 < s2, >0 if s1 > s2. +static inline int mb_strcmp_ic(bool ic, const char *s1, const char *s2) +{ + return (ic ? mb_stricmp(s1, s2) : strcmp(s1, s2)); +} #endif // NVIM_MBYTE_H diff --git a/src/nvim/memline.c b/src/nvim/memline.c index f9d3751390..5ea2397db3 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -992,7 +992,7 @@ void ml_recover(void) if (b0_ff != 0) set_fileformat(b0_ff - 1, OPT_LOCAL); if (b0_fenc != NULL) { - set_option_value((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL); + set_option_value("fenc", 0L, (char *)b0_fenc, OPT_LOCAL); xfree(b0_fenc); } unchanged(curbuf, TRUE); diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 58c01fbe7a..b4fdd86a6d 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -430,6 +430,19 @@ char *xstrdup(const char *str) return xmemdupz(str, strlen(str)); } +/// strdup() wrapper +/// +/// Unlike xstrdup() allocates a new empty string if it receives NULL. +char *xstrdupnul(const char *const str) + FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET +{ + if (str == NULL) { + return xmallocz(0); + } else { + return xstrdup(str); + } +} + /// A version of memchr that starts the search at `src + len`. /// /// Based on glibc's memrchr. diff --git a/src/nvim/message.c b/src/nvim/message.c index 4cd0db21e8..1d3609291a 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -573,16 +573,17 @@ void emsg_invreg(int name) /// Print an error message with unknown number of arguments bool emsgf(const char *const fmt, ...) { + static char errbuf[IOSIZE]; if (emsg_not_now()) { return true; } va_list ap; va_start(ap, fmt); - vim_vsnprintf((char *) IObuff, IOSIZE, fmt, ap, NULL); + vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL); va_end(ap); - return emsg(IObuff); + return emsg((const char_u *)errbuf); } static void msg_emsgf_event(void **argv) @@ -718,7 +719,7 @@ int delete_first_msg(void) void ex_messages(void *const eap_p) FUNC_ATTR_NONNULL_ALL { - exarg_T *eap = (exarg_T *)eap_p; + const exarg_T *const eap = (const exarg_T *)eap_p; struct msg_hist *p; int c = 0; @@ -847,23 +848,22 @@ void wait_return(int redraw) * CTRL-C, but we need to loop then. */ had_got_int = got_int; - /* Don't do mappings here, we put the character back in the - * typeahead buffer. */ - ++no_mapping; - ++allow_keys; + // Don't do mappings here, we put the character back in the + // typeahead buffer. + no_mapping++; - /* Temporarily disable Recording. If Recording is active, the - * character will be recorded later, since it will be added to the - * typebuf after the loop */ + // Temporarily disable Recording. If Recording is active, the + // character will be recorded later, since it will be added to the + // typebuf after the loop save_Recording = Recording; save_scriptout = scriptout; Recording = FALSE; scriptout = NULL; c = safe_vgetc(); - if (had_got_int && !global_busy) - got_int = FALSE; - --no_mapping; - --allow_keys; + if (had_got_int && !global_busy) { + got_int = false; + } + no_mapping--; Recording = save_Recording; scriptout = save_scriptout; @@ -1563,13 +1563,17 @@ void msg_puts_attr(const char *const s, const int attr) msg_puts_attr_len(s, -1, attr); } -/// Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes). -/// When "maxlen" is -1 there is no maximum length. -/// When "maxlen" is >= 0 the message is not put in the history. -void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) +/// Write a message with highlight attributes +/// +/// @param[in] str NUL-terminated message string. +/// @param[in] len Length of the string or -1. +/// @param[in] attr Highlight attribute. +void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) + FUNC_ATTR_NONNULL_ALL { + assert(len < 0 || memchr(str, 0, len) == NULL); // If redirection is on, also write to the redirection file. - redir_write(str, maxlen); + redir_write(str, len); // Don't print anything when using ":silent cmd". if (msg_silent != 0) { @@ -1577,8 +1581,8 @@ void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) } // if MSG_HIST flag set, add message to history - if ((attr & MSG_HIST) && maxlen < 0) { - add_msg_hist(str, -1, attr); + if (attr & MSG_HIST) { + add_msg_hist(str, (int)len, attr); attr &= ~MSG_HIST; } @@ -1597,9 +1601,9 @@ void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) // different, e.g. for Win32 console) or we just don't know where the // cursor is. if (msg_use_printf()) { - msg_puts_printf(str, maxlen); + msg_puts_printf(str, len); } else { - msg_puts_display((const char_u *)str, maxlen, attr, false); + msg_puts_display((const char_u *)str, len, attr, false); } } @@ -1987,7 +1991,7 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) char buf[4]; char *p; - while (*s != NUL && (maxlen < 0 || s - str < maxlen)) { + while ((maxlen < 0 || s - str < maxlen) && *s != NUL) { if (!(silent_mode && p_verbose == 0)) { // NL --> CR NL translation (for Unix, not for "--version") p = &buf[0]; diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 0bb5a8468d..0b74b4437e 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -2211,44 +2211,47 @@ change_warning ( } } -/* - * Ask for a reply from the user, a 'y' or a 'n'. - * No other characters are accepted, the message is repeated until a valid - * reply is entered or CTRL-C is hit. - * If direct is TRUE, don't use vgetc() but ui_inchar(), don't get characters - * from any buffers but directly from the user. - * - * return the 'y' or 'n' - */ -int ask_yesno(const char *str, bool direct) +/// Ask for a reply from the user, 'y' or 'n' +/// +/// No other characters are accepted, the message is repeated until a valid +/// reply is entered or <C-c> is hit. +/// +/// @param[in] str Prompt: question to ask user. Is always followed by +/// " (y/n)?". +/// @param[in] direct Determines what function to use to get user input. If +/// true then ui_inchar() will be used, otherwise vgetc(). +/// I.e. when direct is true then characters are obtained +/// directly from the user without buffers involved. +/// +/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt. +int ask_yesno(const char *const str, const bool direct) { - int r = ' '; - int save_State = State; + const int save_State = State; - ++no_wait_return; - State = CONFIRM; /* mouse behaves like with :confirm */ - setmouse(); /* disables mouse for xterm */ - ++no_mapping; - ++allow_keys; /* no mapping here, but recognize keys */ + no_wait_return++; + State = CONFIRM; // Mouse behaves like with :confirm. + setmouse(); // Disable mouse in xterm. + no_mapping++; + int r = ' '; while (r != 'y' && r != 'n') { - /* same highlighting as for wait_return */ - smsg_attr(hl_attr(HLF_R), - "%s (y/n)?", str); - if (direct) + // Same highlighting as for wait_return. + smsg_attr(hl_attr(HLF_R), "%s (y/n)?", str); + if (direct) { r = get_keystroke(); - else + } else { r = plain_vgetc(); - if (r == Ctrl_C || r == ESC) + } + if (r == Ctrl_C || r == ESC) { r = 'n'; - msg_putchar(r); /* show what you typed */ + } + msg_putchar(r); // Show what you typed. ui_flush(); } - --no_wait_return; + no_wait_return--; State = save_State; setmouse(); - --no_mapping; - --allow_keys; + no_mapping--; return r; } @@ -2398,8 +2401,7 @@ get_number ( if (msg_silent != 0) return 0; - ++no_mapping; - ++allow_keys; /* no mapping here, but recognize keys */ + no_mapping++; for (;; ) { ui_cursor_goto(msg_row, msg_col); c = safe_vgetc(); @@ -2427,8 +2429,7 @@ get_number ( } else if (c == CAR || c == NL || c == Ctrl_C || c == ESC) break; } - --no_mapping; - --allow_keys; + no_mapping--; return n; } diff --git a/src/nvim/move.c b/src/nvim/move.c index 4c3f82bc16..4feabf624b 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1877,6 +1877,7 @@ int onepage(int dir, long count) } } foldAdjustCursor(); + cursor_correct(); check_cursor_col(); if (retval == OK) { beginline(BL_SOL | BL_FIX); diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index de6167c7fc..259dcc523c 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -147,7 +147,7 @@ void channel_from_connection(SocketWatcher *watcher) /// @param name The event name, an arbitrary string /// @param args Array with event arguments /// @return True if the event was sent successfully, false otherwise. -bool channel_send_event(uint64_t id, char *name, Array args) +bool channel_send_event(uint64_t id, const char *name, Array args) { Channel *channel = NULL; @@ -160,7 +160,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) if (channel) { if (channel->pending_requests) { // Pending request, queue the notification for later sending. - String method = cstr_as_string(name); + const String method = cstr_as_string((char *)name); WBuffer *buffer = serialize_request(id, 0, method, args, &out_buffer, 1); kv_push(channel->delayed_notifications, buffer); } else { @@ -182,7 +182,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) /// @param[out] error True if the return value is an error /// @return Whatever the remote method returned Object channel_send_call(uint64_t id, - char *method_name, + const char *method_name, Array args, Error *err) { @@ -519,10 +519,10 @@ static void send_error(Channel *channel, uint64_t id, char *err) static void send_request(Channel *channel, uint64_t id, - char *name, + const char *name, Array args) { - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); channel_write(channel, serialize_request(channel->id, id, method, @@ -532,10 +532,10 @@ static void send_request(Channel *channel, } static void send_event(Channel *channel, - char *name, + const char *name, Array args) { - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); channel_write(channel, serialize_request(channel->id, 0, method, @@ -544,7 +544,7 @@ static void send_event(Channel *channel, 1)); } -static void broadcast_event(char *name, Array args) +static void broadcast_event(const char *name, Array args) { kvec_t(Channel *) subscribed = KV_INITIAL_VALUE; Channel *channel; @@ -560,7 +560,7 @@ static void broadcast_event(char *name, Array args) goto end; } - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); WBuffer *buffer = serialize_request(0, 0, method, @@ -728,7 +728,7 @@ static void call_set_error(Channel *channel, char *msg) static WBuffer *serialize_request(uint64_t channel_id, uint64_t request_id, - String method, + const String method, Array args, msgpack_sbuffer *sbuffer, size_t refcount) diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 5137b375f0..4d8a9984e1 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -114,7 +114,13 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) } break; } - case MSGPACK_OBJECT_FLOAT: { +#ifdef NVIM_MSGPACK_HAS_FLOAT32 + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: +#else + case MSGPACK_OBJECT_FLOAT: +#endif + { STATIC_ASSERT(sizeof(Float) == sizeof(cur.mobj->via.f64), "Msgpack floating-point size does not match API integer"); *cur.aobj = FLOATING_OBJ(cur.mobj->via.f64); @@ -181,7 +187,12 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) case MSGPACK_OBJECT_BOOLEAN: case MSGPACK_OBJECT_POSITIVE_INTEGER: case MSGPACK_OBJECT_NEGATIVE_INTEGER: +#ifdef NVIM_MSGPACK_HAS_FLOAT32 + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: +#else case MSGPACK_OBJECT_FLOAT: +#endif case MSGPACK_OBJECT_EXT: case MSGPACK_OBJECT_MAP: case MSGPACK_OBJECT_ARRAY: { @@ -322,7 +333,7 @@ void msgpack_rpc_from_float(Float result, msgpack_packer *res) msgpack_pack_double(res, result); } -void msgpack_rpc_from_string(String result, msgpack_packer *res) +void msgpack_rpc_from_string(const String result, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_str(res, result.size); @@ -478,7 +489,7 @@ Object msgpack_rpc_handle_invalid_arguments(uint64_t channel_id, /// Serializes a msgpack-rpc request or notification(id == 0) void msgpack_rpc_serialize_request(uint64_t request_id, - String method, + const String method, Array args, msgpack_packer *pac) FUNC_ATTR_NONNULL_ARG(4) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 7188e13436..388ddfc8bb 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -642,8 +642,7 @@ static void normal_get_additional_char(NormalState *s) bool langmap_active = false; // using :lmap mappings int lang; // getting a text character - ++no_mapping; - ++allow_keys; // no mapping for nchar, but allow key codes + no_mapping++; // Don't generate a CursorHold event here, most commands can't handle // it, e.g., nv_replace(), nv_csearch(). did_cursorhold = true; @@ -681,8 +680,7 @@ static void normal_get_additional_char(NormalState *s) } if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP) { // Allow mappings defined with ":lmap". - --no_mapping; - --allow_keys; + no_mapping--; if (repl) { State = LREPLACE; } else { @@ -695,8 +693,7 @@ static void normal_get_additional_char(NormalState *s) if (langmap_active) { // Undo the decrement done above - ++no_mapping; - ++allow_keys; + no_mapping++; State = NORMAL_BUSY; } State = NORMAL_BUSY; @@ -781,8 +778,7 @@ static void normal_get_additional_char(NormalState *s) } no_mapping++; } - --no_mapping; - --allow_keys; + no_mapping--; } static void normal_invert_horizontal(NormalState *s) @@ -832,8 +828,7 @@ static bool normal_get_command_count(NormalState *s) } if (s->ctrl_w) { - ++no_mapping; - ++allow_keys; // no mapping for nchar, but keys + no_mapping++; } ++no_zero_mapping; // don't map zero here @@ -841,8 +836,7 @@ static bool normal_get_command_count(NormalState *s) LANGMAP_ADJUST(s->c, true); --no_zero_mapping; if (s->ctrl_w) { - --no_mapping; - --allow_keys; + no_mapping--; } s->need_flushbuf |= add_to_showcmd(s->c); } @@ -852,12 +846,10 @@ static bool normal_get_command_count(NormalState *s) s->ctrl_w = true; s->ca.opcount = s->ca.count0; // remember first count s->ca.count0 = 0; - ++no_mapping; - ++allow_keys; // no mapping for nchar, but keys + no_mapping++; s->c = plain_vgetc(); // get next character LANGMAP_ADJUST(s->c, true); - --no_mapping; - --allow_keys; + no_mapping--; s->need_flushbuf |= add_to_showcmd(s->c); return true; } @@ -925,9 +917,7 @@ normal_end: checkpcmark(); // check if we moved since setting pcmark xfree(s->ca.searchbuf); - if (has_mbyte) { - mb_adjust_cursor(); - } + mb_check_adjust_col(curwin); // #6203 if (curwin->w_p_scb && s->toplevel) { validate_cursor(); // may need to update w_leftcol @@ -1165,7 +1155,7 @@ static void normal_check_stuff_buffer(NormalState *s) if (need_start_insertmode && goto_im() && !VIsual_active) { need_start_insertmode = false; - stuffReadbuff((uint8_t *)"i"); // start insert mode next + stuffReadbuff("i"); // start insert mode next // skip the fileinfo message now, because it would be shown // after insert mode finishes! need_fileinfo = false; @@ -1479,8 +1469,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * If 'cpoptions' does not contain 'r', insert the search * pattern to really repeat the same command. */ - if (vim_strchr(p_cpo, CPO_REDO) == NULL) + if (vim_strchr(p_cpo, CPO_REDO) == NULL) { AppendToRedobuffLit(cap->searchbuf, -1); + } AppendToRedobuff(NL_STR); } else if (cap->cmdchar == ':') { /* do_cmdline() has stored the first typed line in @@ -1863,10 +1854,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) break; case OP_FILTER: - if (vim_strchr(p_cpo, CPO_FILTER) != NULL) - AppendToRedobuff((char_u *)"!\r"); /* use any last used !cmd */ - else - bangredo = true; /* do_bang() will put cmd in redo buffer */ + if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { + AppendToRedobuff("!\r"); // Use any last used !cmd. + } else { + bangredo = true; // do_bang() will put cmd in redo buffer. + } case OP_INDENT: case OP_COLON: @@ -2036,43 +2028,44 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) static void op_colon(oparg_T *oap) { stuffcharReadbuff(':'); - if (oap->is_VIsual) - stuffReadbuff((char_u *)"'<,'>"); - else { - /* - * Make the range look nice, so it can be repeated. - */ - if (oap->start.lnum == curwin->w_cursor.lnum) + if (oap->is_VIsual) { + stuffReadbuff("'<,'>"); + } else { + // Make the range look nice, so it can be repeated. + if (oap->start.lnum == curwin->w_cursor.lnum) { stuffcharReadbuff('.'); - else + } else { stuffnumReadbuff((long)oap->start.lnum); + } if (oap->end.lnum != oap->start.lnum) { stuffcharReadbuff(','); - if (oap->end.lnum == curwin->w_cursor.lnum) + if (oap->end.lnum == curwin->w_cursor.lnum) { stuffcharReadbuff('.'); - else if (oap->end.lnum == curbuf->b_ml.ml_line_count) + } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { stuffcharReadbuff('$'); - else if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffReadbuff((char_u *)".+"); + } else if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffReadbuff(".+"); stuffnumReadbuff(oap->line_count - 1); - } else + } else { stuffnumReadbuff((long)oap->end.lnum); + } } } - if (oap->op_type != OP_COLON) - stuffReadbuff((char_u *)"!"); + if (oap->op_type != OP_COLON) { + stuffReadbuff("!"); + } if (oap->op_type == OP_INDENT) { - stuffReadbuff(get_equalprg()); - stuffReadbuff((char_u *)"\n"); + stuffReadbuff((const char *)get_equalprg()); + stuffReadbuff("\n"); } else if (oap->op_type == OP_FORMAT) { if (*curbuf->b_p_fp != NUL) { - stuffReadbuff(curbuf->b_p_fp); + stuffReadbuff((const char *)curbuf->b_p_fp); } else if (*p_fp != NUL) { - stuffReadbuff(p_fp); + stuffReadbuff((const char *)p_fp); } else { - stuffReadbuff((char_u *)"fmt"); + stuffReadbuff("fmt"); } - stuffReadbuff((char_u *)"\n']"); + stuffReadbuff("\n']"); } /* @@ -2085,7 +2078,6 @@ static void op_colon(oparg_T *oap) */ static void op_function(oparg_T *oap) { - char_u *(argv[1]); int save_virtual_op = virtual_op; if (*p_opfunc == NUL) @@ -2099,16 +2091,16 @@ static void op_function(oparg_T *oap) decl(&curbuf->b_op_end); } - if (oap->motion_type == kMTBlockWise) { - argv[0] = (char_u *)"block"; - } else if (oap->motion_type == kMTLineWise) { - argv[0] = (char_u *)"line"; - } else { - argv[0] = (char_u *)"char"; - } + const char_u *const argv[1] = { + (const char_u *)(((const char *const[]) { + [kMTBlockWise] = "block", + [kMTLineWise] = "line", + [kMTCharWise] = "char", + })[oap->motion_type]), + }; - /* Reset virtual_op so that 'virtualedit' can be changed in the - * function. */ + // Reset virtual_op so that 'virtualedit' can be changed in the + // function. virtual_op = MAYBE; (void)call_func_retnr(p_opfunc, 1, argv, false); @@ -2315,7 +2307,7 @@ do_mouse ( if (VIsual_active) { if (VIsual_select) { stuffcharReadbuff(Ctrl_G); - stuffReadbuff((char_u *)"\"+p"); + stuffReadbuff("\"+p"); } else { stuffcharReadbuff('y'); stuffcharReadbuff(K_MIDDLEMOUSE); @@ -2475,7 +2467,7 @@ do_mouse ( &rettv, ARRAY_SIZE(argv), argv, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &doesrange, true, NULL, NULL); - clear_tv(&rettv); + tv_clear(&rettv); break; } } @@ -4045,12 +4037,10 @@ static void nv_zet(cmdarg_T *cap) return; n = nchar - '0'; for (;; ) { - ++no_mapping; - ++allow_keys; /* no mapping for nchar, but allow key codes */ + no_mapping++; nchar = plain_vgetc(); LANGMAP_ADJUST(nchar, true); - --no_mapping; - --allow_keys; + no_mapping--; (void)add_to_showcmd(nchar); if (nchar == K_DEL || nchar == K_KDEL) n /= 10; @@ -4378,13 +4368,11 @@ dozet: break; - case 'u': /* "zug" and "zuw": undo "zg" and "zw" */ - ++no_mapping; - ++allow_keys; /* no mapping for nchar, but allow key codes */ + case 'u': // "zug" and "zuw": undo "zg" and "zw" + no_mapping++; nchar = plain_vgetc(); LANGMAP_ADJUST(nchar, true); - --no_mapping; - --allow_keys; + no_mapping--; (void)add_to_showcmd(nchar); if (vim_strchr((char_u *)"gGwW", nchar) == NULL) { clearopbeep(cap->oap); @@ -4491,7 +4479,7 @@ static void nv_colon(cmdarg_T *cap) /* translate "count:" into ":.,.+(count - 1)" */ stuffcharReadbuff('.'); if (cap->count0 > 1) { - stuffReadbuff((char_u *)",.+"); + stuffReadbuff(",.+"); stuffnumReadbuff(cap->count0 - 1L); } } @@ -4682,6 +4670,7 @@ static void nv_ident(cmdarg_T *cap) char_u *kp = *curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp; // 'keywordprg' assert(*kp != NUL); // option.c:do_set() should default to ":help" if empty. bool kp_ex = (*kp == ':'); // 'keywordprg' is an ex command + bool kp_help = (STRCMP(kp, ":he") == 0 || STRCMP(kp, ":help") == 0); size_t buf_size = n * 2 + 30 + STRLEN(kp); char *buf = xmalloc(buf_size); buf[0] = NUL; @@ -4704,7 +4693,9 @@ static void nv_ident(cmdarg_T *cap) break; case 'K': - if (kp_ex) { + 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)); } @@ -4767,11 +4758,11 @@ static void nv_ident(cmdarg_T *cap) } // Now grab the chars in the identifier - if (cmdchar == 'K') { + if (cmdchar == 'K' && !kp_help) { ptr = vim_strnsave(ptr, n); if (kp_ex) { // Escape the argument properly for an Ex command - p = vim_strsave_fnameescape(ptr, false); + p = (char_u *)vim_strsave_fnameescape((const char *)ptr, false); } else { // Escape the argument properly for a shell command p = vim_strsave_shellescape(ptr, true, true); @@ -6171,17 +6162,15 @@ static void nv_abbrev(cmdarg_T *cap) */ static void nv_optrans(cmdarg_T *cap) { - static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh", - (char_u *)"d$", (char_u *)"c$", - (char_u *)"cl", (char_u *)"cc", - (char_u *)"yy", (char_u *)":s\r"}; - static char_u *str = (char_u *)"xXDCsSY&"; + static const char *(ar[]) = { "dl", "dh", "d$", "c$", "cl", "cc", "yy", + ":s\r" }; + static const char *str = "xXDCsSY&"; if (!checkclearopq(cap->oap)) { if (cap->count0) { stuffnumReadbuff(cap->count0); } - stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]); + stuffReadbuff(ar[strchr(str, (char)cap->cmdchar) - str]); } cap->opcount = 0; } @@ -7304,11 +7293,11 @@ static bool unadjust_for_sel(void) pp = &curwin->w_cursor; else pp = &VIsual; - if (pp->coladd > 0) - --pp->coladd; - else if (pp->col > 0) { - --pp->col; - mb_adjustpos(curbuf, pp); + if (pp->coladd > 0) { + pp->coladd--; + } else if (pp->col > 0) { + pp->col--; + mark_mb_adjustpos(curbuf, pp); } else if (pp->lnum > 1) { --pp->lnum; pp->col = (colnr_T)STRLEN(ml_get(pp->lnum)); @@ -7843,7 +7832,7 @@ static void get_op_vcol( // prevent from moving onto a trail byte if (has_mbyte) { - mb_adjustpos(curwin->w_buffer, &oap->end); + mark_mb_adjustpos(curwin->w_buffer, &oap->end); } getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c13b6f736a..68ef27222c 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -14,8 +14,10 @@ #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/assert.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" @@ -41,6 +43,7 @@ #include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/macros.h" #include "nvim/window.h" #include "nvim/os/input.h" #include "nvim/os/time.h" @@ -888,7 +891,7 @@ static void set_yreg_additional_data(yankreg_T *reg, dict_T *additional_data) if (reg->additional_data == additional_data) { return; } - dict_unref(reg->additional_data); + tv_dict_unref(reg->additional_data); reg->additional_data = additional_data; } @@ -1106,7 +1109,6 @@ int insert_reg( ) { int retval = OK; - char_u *arg; int allocated; /* @@ -1122,21 +1124,24 @@ int insert_reg( if (regname != NUL && !valid_yank_reg(regname, false)) return FAIL; - if (regname == '.') /* insert last inserted text */ - retval = stuff_inserted(NUL, 1L, TRUE); - else if (get_spec_reg(regname, &arg, &allocated, TRUE)) { - if (arg == NULL) + char_u *arg; + if (regname == '.') { // Insert last inserted text. + retval = stuff_inserted(NUL, 1L, true); + } else if (get_spec_reg(regname, &arg, &allocated, true)) { + if (arg == NULL) { return FAIL; - stuffescaped(arg, literally); - if (allocated) + } + stuffescaped((const char *)arg, literally); + if (allocated) { xfree(arg); - } else { /* name or number register */ + } + } else { // Name or number register. yankreg_T *reg = get_yank_register(regname, YREG_PASTE); if (reg->y_array == NULL) { retval = FAIL; } else { for (size_t i = 0; i < reg->y_size; i++) { - stuffescaped(reg->y_array[i], literally); + stuffescaped((const char *)reg->y_array[i], literally); // Insert a newline between lines and after last line if // y_type is kMTLineWise. if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { @@ -1153,29 +1158,29 @@ int insert_reg( * Stuff a string into the typeahead buffer, such that edit() will insert it * literally ("literally" TRUE) or interpret is as typed characters. */ -static void stuffescaped(char_u *arg, int literally) +static void stuffescaped(const char *arg, int literally) { - int c; - char_u *start; - while (*arg != NUL) { - /* Stuff a sequence of normal ASCII characters, that's fast. Also - * stuff K_SPECIAL to get the effect of a special key when "literally" - * is TRUE. */ - start = arg; - while ((*arg >= ' ' && *arg < DEL) || (*arg == K_SPECIAL && !literally)) - ++arg; - if (arg > start) + // Stuff a sequence of normal ASCII characters, that's fast. Also + // stuff K_SPECIAL to get the effect of a special key when "literally" + // is TRUE. + const char *const start = arg; + while ((*arg >= ' ' && *arg < DEL) || ((uint8_t)(*arg) == K_SPECIAL + && !literally)) { + arg++; + } + if (arg > start) { stuffReadbuffLen(start, (long)(arg - start)); + } /* stuff a single special character */ if (*arg != NUL) { - if (has_mbyte) - c = mb_cptr2char_adv(&arg); - else - c = *arg++; - if (literally && ((c < ' ' && c != TAB) || c == DEL)) + const int c = (has_mbyte + ? mb_cptr2char_adv((const char_u **)&arg) + : (uint8_t)(*arg++)); + if (literally && ((c < ' ' && c != TAB) || c == DEL)) { stuffcharReadbuff(Ctrl_V); + } stuffcharReadbuff(c); } } @@ -2555,33 +2560,33 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) dict_T *dict = get_vim_var_dict(VV_EVENT); // the yanked text - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(list, reg->y_array[i], -1); + tv_list_append_string(list, (const char *)reg->y_array[i], -1); } list->lv_lock = VAR_FIXED; - dict_add_list(dict, "regcontents", list); + tv_dict_add_list(dict, S_LEN("regcontents"), list); // the register type char buf[NUMBUFLEN+2]; format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); - dict_add_nr_str(dict, "regtype", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("regtype"), buf); // name of requested register or the empty string for an unnamed operation. buf[0] = (char)oap->regname; buf[1] = NUL; - dict_add_nr_str(dict, "regname", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("regname"), buf); // kind of operation (yank/delete/change) buf[0] = (char)get_op_char(oap->op_type); buf[1] = NUL; - dict_add_nr_str(dict, "operator", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("operator"), buf); - dict_set_keys_readonly(dict); + tv_dict_set_keys_readonly(dict); textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); textlock--; - dict_clear(dict); + tv_dict_clear(dict); recursive = false; } @@ -2660,7 +2665,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // back to the previous line in the case of 'noautoindent' and // 'backspace' includes "eol". So we insert a dummy space for Ctrl_U // to consume. - stuffReadbuff((char_u *)"\n "); + stuffReadbuff("\n "); stuffcharReadbuff(Ctrl_U); } } @@ -2672,7 +2677,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // character. Simulate it with motion commands after the insert. if (flags & PUT_CURSEND) { if (flags & PUT_LINE) { - stuffReadbuff((char_u *)"j0"); + stuffReadbuff("j0"); } else { // Avoid ringing the bell from attempting to move into the space after // the current line. We can stuff the readbuffer with "l" if: @@ -2702,7 +2707,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } } else if (flags & PUT_LINE) { - stuffReadbuff((char_u *)"g'["); + stuffReadbuff("g'["); } // So the 'u' command restores cursor position after ".p, save the cursor @@ -4844,8 +4849,8 @@ static void *get_reg_wrap_one_line(char_u *s, int flags) if (!(flags & kGRegList)) { return s; } - list_T *list = list_alloc(); - list_append_string(list, NULL, -1); + list_T *list = tv_list_alloc(); + tv_list_append_string(list, NULL, 0); list->lv_first->li_tv.vval.v_string = s; return list; } @@ -4895,9 +4900,9 @@ void *get_reg_contents(int regname, int flags) return NULL; if (flags & kGRegList) { - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(list, reg->y_array[i], -1); + tv_list_append_string(list, (const char *)reg->y_array[i], -1); } return list; @@ -4978,7 +4983,7 @@ void write_reg_contents(int name, const char_u *str, ssize_t len, write_reg_contents_ex(name, str, len, must_append, kMTUnknown, 0L); } -void write_reg_contents_lst(int name, char_u **strings, int maxlen, +void write_reg_contents_lst(int name, char_u **strings, bool must_append, MotionType yank_type, colnr_T block_len) { @@ -5482,17 +5487,19 @@ void cursor_pos_info(dict_T *dict) if (dict != NULL) { // Don't shorten this message, the user asked for it. - dict_add_nr_str(dict, "words", word_count, NULL); - dict_add_nr_str(dict, "chars", char_count, NULL); - dict_add_nr_str(dict, "bytes", byte_count + bom_count, NULL); - - dict_add_nr_str(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes", - byte_count_cursor, NULL); - dict_add_nr_str(dict, l_VIsual_active ? "visual_chars" : "cursor_chars", - char_count_cursor, NULL); - dict_add_nr_str(dict, l_VIsual_active ? "visual_words" : "cursor_words", - word_count_cursor, NULL); - } + tv_dict_add_nr(dict, S_LEN("words"), (varnumber_T)word_count); + tv_dict_add_nr(dict, S_LEN("chars"), (varnumber_T)char_count); + tv_dict_add_nr(dict, S_LEN("bytes"), (varnumber_T)(byte_count + bom_count)); + + STATIC_ASSERT(sizeof("visual") == sizeof("cursor"), + "key_len argument in tv_dict_add_nr is wrong"); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes", + sizeof("visual_bytes") - 1, (varnumber_T)byte_count_cursor); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_chars" : "cursor_chars", + sizeof("visual_chars") - 1, (varnumber_T)char_count_cursor); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_words" : "cursor_words", + sizeof("visual_words") - 1, (varnumber_T)word_count_cursor); + } } /// Check if the default register (used in an unnamed paste) should be a @@ -5570,9 +5577,9 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) } free_register(reg); - list_T *args = list_alloc(); - char_u regname = (char_u)name; - list_append_string(args, ®name, 1); + list_T *const args = tv_list_alloc(); + const char regname = (char)name; + tv_list_append_string(args, ®name, 1); typval_T result = eval_call_provider("clipboard", "get", args); @@ -5584,7 +5591,8 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) goto err; } - list_T *res = result.vval.v_list, *lines = NULL; + list_T *res = result.vval.v_list; + list_T *lines = NULL; if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) { lines = res->lv_first->li_tv.vval.v_list; if (res->lv_last->li_tv.v_type != VAR_STRING) { @@ -5628,7 +5636,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) if (li->li_tv.v_type != VAR_STRING) { goto err; } - reg->y_array[i++] = (uint8_t *)xstrdup((char *)li->li_tv.vval.v_string); + reg->y_array[i++] = (char_u *)xstrdupnul((char *)li->li_tv.vval.v_string); } if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) { @@ -5686,35 +5694,39 @@ static void set_clipboard(int name, yankreg_T *reg) return; } - list_T *lines = list_alloc(); + list_T *lines = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(lines, reg->y_array[i], -1); + tv_list_append_string(lines, (const char *)reg->y_array[i], -1); } - list_T *args = list_alloc(); - list_append_list(args, lines); + list_T *args = tv_list_alloc(); + tv_list_append_list(args, lines); - char_u regtype; + char regtype; switch (reg->y_type) { - case kMTLineWise: - regtype = 'V'; - list_append_string(lines, (char_u*)"", 0); - break; - case kMTCharWise: - regtype = 'v'; - break; - case kMTBlockWise: - regtype = 'b'; - list_append_string(lines, (char_u*)"", 0); - break; - case kMTUnknown: - assert(false); - } - list_append_string(args, ®type, 1); - - char_u regname = (char_u)name; - list_append_string(args, ®name, 1); + case kMTLineWise: { + regtype = 'V'; + tv_list_append_string(lines, NULL, 0); + break; + } + case kMTCharWise: { + regtype = 'v'; + break; + } + case kMTBlockWise: { + regtype = 'b'; + tv_list_append_string(lines, NULL, 0); + break; + } + case kMTUnknown: { + assert(false); + } + } + tv_list_append_string(args, ®type, 1); + + const char regname = (char)name; + tv_list_append_string(args, ®name, 1); (void)eval_call_provider("clipboard", "set", args); } diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 44df2e9e0c..13d0142343 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -6,7 +6,7 @@ #include "nvim/macros.h" #include "nvim/ascii.h" #include "nvim/types.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/os/time.h" typedef int (*Indenter)(void); diff --git a/src/nvim/option.c b/src/nvim/option.c index 2a17ca69d1..458d80716c 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -25,6 +25,7 @@ #include <limits.h> #include "nvim/vim.h" +#include "nvim/macros.h" #include "nvim/ascii.h" #include "nvim/edit.h" #include "nvim/option.h" @@ -34,6 +35,7 @@ #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" @@ -156,7 +158,6 @@ static long p_ts; static long p_tw; static int p_udf; static long p_wm; -static long p_scbk; static char_u *p_keymap; /* Saved values for when 'bin' is set. */ @@ -244,7 +245,8 @@ typedef struct vimoption { "A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal," \ "B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel," \ "x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill," \ - "!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine" + "!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine," \ + "0:Whitespace" /* * options[] is initialized here. @@ -605,7 +607,7 @@ void set_init_1(void) /* * 'maxmemtot' and 'maxmem' may have to be adjusted for available memory */ - opt_idx = findoption((char_u *)"maxmemtot"); + opt_idx = findoption("maxmemtot"); if (opt_idx >= 0) { { /* Use half of amount of memory available to Vim. */ @@ -615,7 +617,7 @@ void set_init_1(void) ? UINTPTR_MAX : (uintptr_t)(available_kib /2); options[opt_idx].def_val[VI_DEFAULT] = (char_u *)n; - opt_idx = findoption((char_u *)"maxmem"); + opt_idx = findoption("maxmem"); if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = (char_u *)n; } @@ -646,7 +648,7 @@ void set_init_1(void) } } buf[j] = NUL; - opt_idx = findoption((char_u *)"cdpath"); + opt_idx = findoption("cdpath"); if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = buf; options[opt_idx].flags |= P_DEF_ALLOCED; @@ -765,8 +767,9 @@ void set_init_1(void) * NOTE: mlterm's author is being asked to 'set' a variable * instead of an environment variable due to inheritance. */ - if (os_env_exists("MLTERM")) - set_option_value((char_u *)"tbidi", 1L, NULL, 0); + if (os_env_exists("MLTERM")) { + set_option_value("tbidi", 1L, NULL, 0); + } didset_options2(); @@ -776,7 +779,7 @@ void set_init_1(void) char_u *p = enc_locale(); if (p == NULL) { // use utf-8 as 'default' if locale encoding can't be detected. - p = vim_strsave((char_u *)"utf-8"); + p = (char_u *)xmemdupz(S_LEN("utf-8")); } fenc_default = p; @@ -883,7 +886,7 @@ set_options_default ( static void set_string_default(const char *name, char *val, bool allocated) FUNC_ATTR_NONNULL_ALL { - int opt_idx = findoption((char_u *)name); + int opt_idx = findoption(name); if (opt_idx >= 0) { if (options[opt_idx].flags & P_DEF_ALLOCED) { xfree(options[opt_idx].def_val[VI_DEFAULT]); @@ -905,9 +908,10 @@ void set_number_default(char *name, long val) { int opt_idx; - opt_idx = findoption((char_u *)name); - if (opt_idx >= 0) + opt_idx = findoption(name); + if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = (char_u *)val; + } } #if defined(EXITFREE) @@ -935,11 +939,8 @@ void free_all_options(void) #endif -/* - * Initialize the options, part two: After getting Rows and Columns and - * setting 'term'. - */ -void set_init_2(void) +/// Initialize the options, part two: After getting Rows and Columns. +void set_init_2(bool headless) { int idx; @@ -948,20 +949,26 @@ void set_init_2(void) * wrong when the window height changes. */ set_number_default("scroll", Rows / 2); - idx = findoption((char_u *)"scroll"); - if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) + idx = findoption("scroll"); + if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { set_option_default(idx, OPT_LOCAL, p_cp); + } comp_col(); /* * 'window' is only for backwards compatibility with Vi. * Default is Rows - 1. */ - if (!option_was_set((char_u *)"window")) + if (!option_was_set("window")) { p_window = Rows - 1; + } set_number_default("window", Rows - 1); - parse_shape_opt(SHAPE_CURSOR); /* set cursor shapes from 'guicursor' */ - (void)parse_printoptions(); /* parse 'printoptions' default value */ + if (!headless && !os_term_is_nice()) { + set_string_option_direct((char_u *)"guicursor", -1, (char_u *)"", + OPT_GLOBAL, SID_NONE); + } + parse_shape_opt(SHAPE_CURSOR); // set cursor shapes from 'guicursor' + (void)parse_printoptions(); // parse 'printoptions' default value } /* @@ -977,16 +984,18 @@ void set_init_3(void) int idx_sp; int do_sp; - idx_srr = findoption((char_u *)"srr"); - if (idx_srr < 0) - do_srr = FALSE; - else + idx_srr = findoption("srr"); + if (idx_srr < 0) { + do_srr = false; + } else { do_srr = !(options[idx_srr].flags & P_WAS_SET); - idx_sp = findoption((char_u *)"sp"); - if (idx_sp < 0) - do_sp = FALSE; - else + } + idx_sp = findoption("sp"); + if (idx_sp < 0) { + do_sp = false; + } else { do_sp = !(options[idx_sp].flags & P_WAS_SET); + } size_t len = 0; char_u *p = (char_u *)invocation_path_tail(p_sh, &len); @@ -1030,7 +1039,7 @@ void set_init_3(void) } if (bufempty()) { - int idx_ffs = findoption((char_u *)"ffs"); + int idx_ffs = findoption_len(S_LEN("ffs")); // Apply the first entry of 'fileformats' to the initial buffer. if (idx_ffs >= 0 && (options[idx_ffs].flags & P_WAS_SET)) { @@ -1047,16 +1056,20 @@ void set_init_3(void) */ void set_helplang_default(const char *lang) { - int idx; + if (lang == NULL) { + return; + } - if (lang == NULL || STRLEN(lang) < 2) /* safety check */ + const size_t lang_len = strlen(lang); + if (lang_len < 2) { // safety check return; - idx = findoption((char_u *)"hlg"); + } + int idx = findoption("hlg"); if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { if (options[idx].flags & P_ALLOCED) free_string_option(p_hlg); - p_hlg = (char_u *)xstrdup(lang); - /* zh_CN becomes "cn", zh_TW becomes "tw". */ + p_hlg = (char_u *)xmemdupz(lang, lang_len); + // zh_CN becomes "cn", zh_TW becomes "tw". if (STRNICMP(p_hlg, "zh_", 3) == 0 && STRLEN(p_hlg) >= 5) { p_hlg[0] = (char_u)TOLOWER_ASC(p_hlg[3]); p_hlg[1] = (char_u)TOLOWER_ASC(p_hlg[4]); @@ -1083,12 +1096,12 @@ void set_title_defaults(void) * icon name. Saves a bit of time, because the X11 display server does * not need to be contacted. */ - idx1 = findoption((char_u *)"title"); + idx1 = findoption("title"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { options[idx1].def_val[VI_DEFAULT] = (char_u *)(intptr_t)0; p_title = 0; } - idx1 = findoption((char_u *)"icon"); + idx1 = findoption("icon"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { options[idx1].def_val[VI_DEFAULT] = (char_u *)(intptr_t)0; p_icon = 0; @@ -1194,7 +1207,7 @@ do_set ( goto skip; } if (arg[1] == 't' && arg[2] == '_') { // could be term code - opt_idx = findoption_len(arg + 1, (size_t) (len - 1)); + opt_idx = findoption_len((const char *)arg + 1, (size_t)(len - 1)); } len++; if (opt_idx == -1) { @@ -1210,7 +1223,7 @@ do_set ( len++; } } - opt_idx = findoption_len(arg, (size_t) len); + opt_idx = findoption_len((const char *)arg, (size_t)len); if (opt_idx == -1) { key = find_key_option(arg); } @@ -1392,11 +1405,10 @@ do_set ( value = prefix; } - errmsg = set_bool_option(opt_idx, varp, (int)value, - opt_flags); - } else { /* numeric or string */ - if (vim_strchr((char_u *)"=:&<", nextchar) == NULL - || prefix != 1) { + errmsg = (char_u *)set_bool_option(opt_idx, varp, (int)value, + opt_flags); + } else { // Numeric or string. + if (strchr("=:&<", nextchar) == NULL || prefix != 1) { errmsg = e_invarg; goto skip; } @@ -1449,15 +1461,19 @@ do_set ( goto skip; } - if (adding) + if (adding) { value = *(long *)varp + value; - if (prepending) + } + if (prepending) { value = *(long *)varp * value; - if (removing) + } + if (removing) { value = *(long *)varp - value; - errmsg = set_num_option(opt_idx, varp, value, - errbuf, sizeof(errbuf), opt_flags); - } else if (opt_idx >= 0) { /* string */ + } + errmsg = (char_u *)set_num_option(opt_idx, varp, value, + errbuf, sizeof(errbuf), + opt_flags); + } else if (opt_idx >= 0) { // String. char_u *save_arg = NULL; char_u *s = NULL; char_u *oldval = NULL; // previous value if *varp @@ -2222,7 +2238,7 @@ static void check_string_option(char_u **pp) */ int was_set_insecurely(char_u *opt, int opt_flags) { - int idx = findoption(opt); + int idx = findoption((const char *)opt); if (idx >= 0) { uint32_t *flagp = insecure_flag(idx, opt_flags); @@ -2284,9 +2300,9 @@ set_string_option_direct ( int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; int idx = opt_idx; - if (idx == -1) { /* use name */ - idx = findoption(name); - if (idx < 0) { /* not found (should not happen) */ + if (idx == -1) { // Use name. + idx = findoption((const char *)name); + if (idx < 0) { // Not found (should not happen). EMSG2(_(e_intern2), "set_string_option_direct()"); EMSG2(_("For option %s"), name); return; @@ -2570,7 +2586,7 @@ did_set_string_option ( // The color scheme must have set 'background' back to another // value, that's not what we want here. Disable the color // scheme and set the colors again. - do_unlet((char_u *)"g:colors_name", true); + do_unlet(S_LEN("g:colors_name"), true); free_string_option(p_bg); p_bg = vim_strsave((char_u *)(dark ? "dark" : "light")); check_string_option(&p_bg); @@ -2766,7 +2782,7 @@ did_set_string_option ( // option. opt_idx = ((options[opt_idx].fullname[0] == 'v') ? (shada_idx == -1 - ? ((shada_idx = findoption((char_u *) "shada"))) + ? ((shada_idx = findoption("shada"))) : shada_idx) : opt_idx); // Update free_oldval now that we have the opt_idx for 'shada', otherwise @@ -2827,9 +2843,10 @@ did_set_string_option ( } } - /* 'guicursor' */ - else if (varp == &p_guicursor) + // 'guicursor' + else if (varp == &p_guicursor) { errmsg = parse_shape_opt(SHAPE_CURSOR); + } else if (varp == &p_popt) errmsg = parse_printoptions(); @@ -3407,15 +3424,18 @@ static char_u *set_chars_option(char_u **varp) && p[len] == ':' && p[len + 1] != NUL) { s = p + len + 1; - c1 = mb_ptr2char_adv(&s); - if (mb_char2cells(c1) > 1) + c1 = mb_ptr2char_adv((const char_u **)&s); + if (mb_char2cells(c1) > 1) { continue; + } if (tab[i].cp == &lcs_tab2) { - if (*s == NUL) + if (*s == NUL) { continue; - c2 = mb_ptr2char_adv(&s); - if (mb_char2cells(c2) > 1) + } + c2 = mb_ptr2char_adv((const char_u **)&s); + if (mb_char2cells(c2) > 1) { continue; + } } if (*s == ',' || *s == NUL) { if (round) { @@ -3576,24 +3596,24 @@ static void set_option_scriptID_idx(int opt_idx, int opt_flags, int id) } } -/* - * Set the value of a boolean option, and take care of side effects. - * Returns NULL for success, or an error message for an error. - */ -static char_u * -set_bool_option ( - int opt_idx, /* index in options[] table */ - char_u *varp, /* pointer to the option variable */ - int value, /* new value */ - int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */ -) +/// Set the value of a boolean option, taking care of side effects +/// +/// @param[in] opt_idx Option index in options[] table. +/// @param[out] varp Pointer to the option variable. +/// @param[in] value New value. +/// @param[in] opt_flags OPT_LOCAL and/or OPT_GLOBAL. +/// +/// @return NULL on success, error message on error. +static char *set_bool_option(const int opt_idx, char_u *const varp, + const int value, + const int opt_flags) { int old_value = *(int *)varp; /* Disallow changing some options from secure mode */ if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) { - return e_secure; + return (char *)e_secure; } *(int *)varp = value; /* set the new value */ @@ -3606,20 +3626,24 @@ set_bool_option ( *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value; // Ensure that options set to p_force_on cannot be disabled. - if ((int *)varp == &p_force_on && p_force_on == FALSE) { - p_force_on = TRUE; - return e_unsupportedoption; - } + if ((int *)varp == &p_force_on && p_force_on == false) { + p_force_on = true; + return (char *)e_unsupportedoption; // Ensure that options set to p_force_off cannot be enabled. - else if ((int *)varp == &p_force_off && p_force_off == TRUE) { - p_force_off = FALSE; - return e_unsupportedoption; - } - /* 'undofile' */ - else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { - /* Only take action when the option was set. When reset we do not - * delete the undo file, the option may be set again without making - * any changes in between. */ + } else if ((int *)varp == &p_force_off && p_force_off == true) { + p_force_off = false; + return (char *)e_unsupportedoption; + } else if ((int *)varp == &p_lrm) { + // 'langremap' -> !'langnoremap' + p_lnr = !p_lrm; + } else if ((int *)varp == &p_lnr) { + // 'langnoremap' -> !'langremap' + p_lrm = !p_lnr; + // 'undofile' + } else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { + // Only take action when the option was set. When reset we do not + // delete the undo file, the option may be set again without making + // any changes in between. if (curbuf->b_p_udf || p_udf) { char_u hash[UNDO_HASH_SIZE]; buf_T *save_curbuf = curbuf; @@ -3741,8 +3765,8 @@ set_bool_option ( if (curwin->w_p_pvw) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { if (win->w_p_pvw && win != curwin) { - curwin->w_p_pvw = FALSE; - return (char_u *)N_("E590: A preview window already exists"); + curwin->w_p_pvw = false; + return N_("E590: A preview window already exists"); } } } @@ -3890,9 +3914,8 @@ set_bool_option ( /* set 'delcombine' */ p_deco = TRUE; - /* Force-set the necessary keymap for arabic */ - set_option_value((char_u *)"keymap", 0L, (char_u *)"arabic", - OPT_LOCAL); + // Force-set the necessary keymap for arabic. + set_option_value("keymap", 0L, "arabic", OPT_LOCAL); p_altkeymap = 0; p_hkmap = 0; p_fkmap = 0; @@ -3958,20 +3981,18 @@ set_bool_option ( return NULL; } -/* - * Set the value of a number option, and take care of side effects. - * Returns NULL for success, or an error message for an error. - */ -static char_u * -set_num_option ( - int opt_idx, /* index in options[] table */ - char_u *varp, /* pointer to the option variable */ - long value, /* new value */ - char_u *errbuf, /* buffer for error messages */ - size_t errbuflen, /* length of "errbuf" */ - int opt_flags /* OPT_LOCAL, OPT_GLOBAL and - OPT_MODELINE */ -) +/// Set the value of a number option, taking care of side effects +/// +/// @param[in] opt_idx Option index in options[] table. +/// @param[out] varp Pointer to the option variable. +/// @param[in] value New value. +/// @param errbuf Buffer for error messages. +/// @param[in] errbuflen Length of `errbuf`. +/// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE. +/// +/// @return NULL on success, error message on error. +static char *set_num_option(int opt_idx, char_u *varp, long value, + char_u *errbuf, size_t errbuflen, int opt_flags) { char_u *errmsg = NULL; long old_value = *(long *)varp; @@ -3982,7 +4003,7 @@ set_num_option ( /* Disallow changing some options from secure mode. */ if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) { - return e_secure; + return (char *)e_secure; } *pp = value; @@ -4199,16 +4220,13 @@ set_num_option ( FOR_ALL_TAB_WINDOWS(tp, wp) { check_colorcolumn(wp); } - } else if (pp == &curbuf->b_p_scbk) { + } else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) { // 'scrollback' - if (!curbuf->terminal) { + if (*pp < -1 || *pp > SB_MAX + || (opt_flags == OPT_LOCAL && !curbuf->terminal)) { errmsg = e_invarg; - curbuf->b_p_scbk = -1; - } else { - if (curbuf->b_p_scbk < -1 || curbuf->b_p_scbk > 100000) { - errmsg = e_invarg; - curbuf->b_p_scbk = 1000; - } + *pp = old_value; + } else if (curbuf->terminal) { // Force the scrollback to take effect. terminal_resize(curbuf->terminal, UINT16_MAX, UINT16_MAX); } @@ -4255,8 +4273,9 @@ set_num_option ( cmdline_row = (int)(Rows - p_ch); } } - if (p_window >= Rows || !option_was_set((char_u *)"window")) + if (p_window >= Rows || !option_was_set("window")) { p_window = Rows - 1; + } } if (curbuf->b_p_ts <= 0) { @@ -4331,6 +4350,11 @@ set_num_option ( if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = *pp; + if (pp == &curbuf->b_p_scbk && !curbuf->terminal) { + // Normal buffer: reset local 'scrollback' after updating the global value. + curbuf->b_p_scbk = -1; + } + options[opt_idx].flags |= P_WAS_SET; if (!starting && errmsg == NULL) { @@ -4356,7 +4380,7 @@ set_num_option ( curwin->w_set_curswant = TRUE; check_redraw(options[opt_idx].flags); - return errmsg; + return (char *)errmsg; } /* @@ -4394,39 +4418,36 @@ static void check_redraw(uint32_t flags) /// @param[in] len Length of the option. /// /// @return Index of the option or -1 if option was not found. -int findoption_len(const char_u *const arg, const size_t len) +int findoption_len(const char *const arg, const size_t len) { - char *s, *p; + const char *s; + const char *p; static int quick_tab[27] = { 0, 0 }; // quick access table - int is_term_opt; - /* - * For first call: Initialize the quick-access table. - * It contains the index for the first option that starts with a certain - * letter. There are 26 letters, plus the first "t_" option. - */ + // For first call: Initialize the quick-access table. + // It contains the index for the first option that starts with a certain + // letter. There are 26 letters, plus the first "t_" option. if (quick_tab[1] == 0) { p = options[0].fullname; for (short int i = 1; (s = options[i].fullname) != NULL; i++) { if (s[0] != p[0]) { - if (s[0] == 't' && s[1] == '_') + if (s[0] == 't' && s[1] == '_') { quick_tab[26] = i; - else + } else { quick_tab[CharOrdLow(s[0])] = i; + } } p = s; } } - /* - * Check for name starting with an illegal character. - */ + // Check for name starting with an illegal character. if (len == 0 || arg[0] < 'a' || arg[0] > 'z') { return -1; } int opt_idx; - is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); + const bool is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); if (is_term_opt) { opt_idx = quick_tab[26]; } else { @@ -4434,7 +4455,7 @@ int findoption_len(const char_u *const arg, const size_t len) } // Match full name for (; (s = options[opt_idx].fullname) != NULL; opt_idx++) { - if (STRNCMP(arg, s, len) == 0 && s[len] == NUL) { + if (strncmp(arg, s, len) == 0 && s[len] == NUL) { break; } } @@ -4443,20 +4464,22 @@ int findoption_len(const char_u *const arg, const size_t len) // Match short name for (; options[opt_idx].fullname != NULL; opt_idx++) { s = options[opt_idx].shortname; - if (s != NULL && STRNCMP(arg, s, len) == 0 && s[len] == NUL) { + if (s != NULL && strncmp(arg, s, len) == 0 && s[len] == NUL) { break; } s = NULL; } } - if (s == NULL) + if (s == NULL) { opt_idx = -1; + } return opt_idx; } -bool is_tty_option(char *name) +bool is_tty_option(const char *name) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return (name[0] == 't' && name[1] == '_') || !strcmp((char *)name, "term"); + return (name[0] == 't' && name[1] == '_') || strcmp(name, "term") == 0; } #define TCO_BUFFER_SIZE 8 @@ -4492,7 +4515,7 @@ bool get_tty_option(char *name, char **value) return false; } -bool set_tty_option(char *name, char *value) +bool set_tty_option(const char *name, const char *value) { if (!strcmp(name, "t_Co")) { int colors = atoi(value); @@ -4503,23 +4526,24 @@ bool set_tty_option(char *name, char *value) if (colors != t_colors) { t_colors = colors; // We now have a different color setup, initialize it again. - init_highlight(TRUE, FALSE); + init_highlight(true, false); } return true; } - return is_tty_option(name) || !strcmp(name, "term") - || !strcmp(name, "ttytype"); + return (is_tty_option(name) || !strcmp(name, "term") + || !strcmp(name, "ttytype")); } -/* - * Find index for option 'arg'. - * Return -1 if not found. - */ -static int findoption(char_u *arg) +/// Find index for an option +/// +/// @param[in] arg Option name. +/// +/// @return Option index or -1 if option was not found. +static int findoption(const char *const arg) { - return findoption_len(arg, STRLEN(arg)); + return findoption_len(arg, strlen(arg)); } /* @@ -4547,9 +4571,10 @@ get_option_value ( int opt_idx; char_u *varp; - opt_idx = findoption(name); - if (opt_idx < 0) /* unknown option */ + opt_idx = findoption((const char *)name); + if (opt_idx < 0) { // Unknown option. return -3; + } varp = get_varp_scope(&(options[opt_idx]), opt_flags); @@ -4586,7 +4611,7 @@ get_option_value ( // // Pretends that option is absent if it is not present in the requested scope // (i.e. has no global, window-local or buffer-local value depending on -// opt_type). Uses +// opt_type). // // Returned flags: // 0 hidden or unknown option, also option that does not have requested @@ -4605,14 +4630,13 @@ int get_option_value_strict(char *name, } char_u *varp = NULL; - vimoption_T *p; int rv = 0; - int opt_idx = findoption((uint8_t *)name); + int opt_idx = findoption(name); if (opt_idx < 0) { return 0; } - p = &(options[opt_idx]); + vimoption_T *p = &options[opt_idx]; // Hidden option if (p->var == NULL) { @@ -4628,26 +4652,25 @@ int get_option_value_strict(char *name, } if (p->indir == PV_NONE) { - if (opt_type == SREQ_GLOBAL) + if (opt_type == SREQ_GLOBAL) { rv |= SOPT_GLOBAL; - else - return 0; // Did not request global-only option + } else { + return 0; // Did not request global-only option + } } else { if (p->indir & PV_BOTH) { rv |= SOPT_GLOBAL; - } else if (opt_type == SREQ_GLOBAL) { - return 0; // Requested global option } if (p->indir & PV_WIN) { if (opt_type == SREQ_BUF) { - return 0; // Did not request window-local option + return 0; // Requested buffer-local, not window-local option } else { rv |= SOPT_WIN; } } else if (p->indir & PV_BUF) { if (opt_type == SREQ_WIN) { - return 0; // Did not request buffer-local option + return 0; // Requested window-local, not buffer-local option } else { rv |= SOPT_BUF; } @@ -4659,7 +4682,11 @@ int get_option_value_strict(char *name, } if (opt_type == SREQ_GLOBAL) { - varp = p->var; + if (p->var == VAR_WIN) { + return 0; + } else { + varp = p->var; + } } else { if (opt_type == SREQ_BUF) { // Special case: 'modified' is b_changed, but we also want to @@ -4701,21 +4728,19 @@ int get_option_value_strict(char *name, return rv; } -/* - * Set the value of option "name". - * Use "string" for string options, use "number" for other options. - * - * Returns NULL on success or error message on error. - */ -char_u * -set_option_value ( - char_u *name, - long number, - char_u *string, - int opt_flags /* OPT_LOCAL or 0 (both) */ -) +/// Set the value of an option +/// +/// @param[in] name Option name. +/// @param[in] number New value for the number or boolean option. +/// @param[in] string New value for string option. +/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). +/// +/// @return NULL on success, error message on error. +char *set_option_value(const char *const name, const long number, + const char *const string, const int opt_flags) + FUNC_ATTR_NONNULL_ARG(1) { - if (set_tty_option((char *)name, (char *)string)) { + if (set_tty_option(name, string)) { return NULL; } @@ -4723,9 +4748,9 @@ set_option_value ( char_u *varp; opt_idx = findoption(name); - if (opt_idx < 0) + if (opt_idx < 0) { EMSG2(_("E355: Unknown option: %s"), name); - else { + } else { uint32_t flags = options[opt_idx].flags; // Disallow changing some options in the sandbox if (sandbox > 0 && (flags & P_SECURE)) { @@ -4733,11 +4758,11 @@ set_option_value ( return NULL; } if (flags & P_STRING) { - const char *s = (const char *)string; + const char *s = string; if (s == NULL) { s = ""; } - return (char_u *)set_string_option(opt_idx, s, opt_flags); + return set_string_option(opt_idx, s, opt_flags); } else { varp = get_varp_scope(&(options[opt_idx]), opt_flags); if (varp != NULL) { /* hidden option is not changed */ @@ -4756,12 +4781,11 @@ set_option_value ( return NULL; // do nothing as we hit an error } } - if (flags & P_NUM) - return set_num_option(opt_idx, varp, number, - NULL, 0, opt_flags); - else - return set_bool_option(opt_idx, varp, (int)number, - opt_flags); + if (flags & P_NUM) { + return set_num_option(opt_idx, varp, number, NULL, 0, opt_flags); + } else { + return set_bool_option(opt_idx, varp, (int)number, opt_flags); + } } } } @@ -4772,9 +4796,10 @@ char_u *get_highlight_default(void) { int i; - i = findoption((char_u *)"hl"); - if (i >= 0) + i = findoption("hl"); + if (i >= 0) { return options[i].def_val[VI_DEFAULT]; + } return (char_u *)NULL; } @@ -5211,7 +5236,7 @@ void unset_global_local_option(char *name, void *from) vimoption_T *p; buf_T *buf = (buf_T *)from; - int opt_idx = findoption((uint8_t *)name); + int opt_idx = findoption(name); if (opt_idx < 0) { EMSG2(_("E355: Unknown option: %s"), name); return; @@ -5774,11 +5799,12 @@ void reset_modifiable(void) { int opt_idx; - curbuf->b_p_ma = FALSE; - p_ma = FALSE; - opt_idx = findoption((char_u *)"ma"); - if (opt_idx >= 0) - options[opt_idx].def_val[VI_DEFAULT] = FALSE; + curbuf->b_p_ma = false; + p_ma = false; + opt_idx = findoption("ma"); + if (opt_idx >= 0) { + options[opt_idx].def_val[VI_DEFAULT] = false; + } } /* @@ -5876,15 +5902,15 @@ set_context_in_set_cmd ( expand_option_name[2] = p[-2]; expand_option_name[3] = p[-1]; } else { - /* Allow * wildcard */ - while (ASCII_ISALNUM(*p) || *p == '_' || *p == '*') + // Allow * wildcard. + while (ASCII_ISALNUM(*p) || *p == '_' || *p == '*') { p++; - if (*p == NUL) + } + if (*p == NUL) { return; + } nextchar = *p; - *p = NUL; - opt_idx = findoption(arg); - *p = nextchar; + opt_idx = findoption_len((const char *)arg, (size_t)(p - arg)); if (opt_idx == -1 || options[opt_idx].var == NULL) { xp->xp_context = EXPAND_NOTHING; return; @@ -6046,7 +6072,7 @@ void ExpandOldSetting(int *num_file, char_u ***file) * For a terminal key code expand_option_idx is < 0. */ if (expand_option_idx < 0) { - expand_option_idx = findoption(expand_option_name); + expand_option_idx = findoption((const char *)expand_option_name); } if (expand_option_idx >= 0) { @@ -6446,20 +6472,22 @@ void vimrc_found(char_u *fname, char_u *envname) } } -/* - * Return TRUE when option "name" has been set. - * Only works correctly for global options. - */ -int option_was_set(char_u *name) +/// Check whether global option has been set +/// +/// @param[in] name Option name. +/// +/// @return True if it was set. +static bool option_was_set(const char *name) { int idx; idx = findoption(name); - if (idx < 0) /* unknown option */ - return FALSE; - if (options[idx].flags & P_WAS_SET) - return TRUE; - return FALSE; + if (idx < 0) { // Unknown option. + return false; + } else if (options[idx].flags & P_WAS_SET) { + return true; + } + return false; } /* @@ -6875,8 +6903,8 @@ void set_fileformat(int eol_style, int opt_flags) need_maketitle = true; // Set window title later. } -/// Skip to next part of an option argument: Skip space and comma. -char_u *skip_to_option_part(char_u *p) +/// Skip to next part of an option argument: skip space and comma +char_u *skip_to_option_part(const char_u *p) { if (*p == ',') { p++; @@ -6884,7 +6912,7 @@ char_u *skip_to_option_part(char_u *p) while (*p == ' ') { p++; } - return p; + return (char_u *)p; } /// Isolate one part of a string option separated by `sep_chars`. @@ -6944,10 +6972,11 @@ bool signcolumn_on(win_T *wp) return wp->w_buffer->b_signlist != NULL; } -/// Get window or buffer local options. -dict_T * get_winbuf_options(int bufopt) +/// Get window or buffer local options +dict_T *get_winbuf_options(const int bufopt) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { - dict_T *d = dict_alloc(); + dict_T *const d = tv_dict_alloc(); for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) { struct vimoption *opt = &options[opt_idx]; @@ -6958,9 +6987,10 @@ dict_T * get_winbuf_options(int bufopt) if (varp != NULL) { if (opt->flags & P_STRING) { - dict_add_nr_str(d, opt->fullname, 0L, *(char_u **)varp); + tv_dict_add_str(d, opt->fullname, strlen(opt->fullname), + *(const char **)varp); } else { - dict_add_nr_str(d, opt->fullname, *varp, NULL); + tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *varp); } } } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index b171b23edb..4ee0f4f225 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -16,9 +16,9 @@ #define SOPT_UNSET 0x40 // Option does not have local value set // Option types for various functions in option.c -#define SREQ_GLOBAL 0 // Request global option -#define SREQ_WIN 1 // Request window-local option -#define SREQ_BUF 2 // Request buffer-local option +#define SREQ_GLOBAL 0 // Request global option value +#define SREQ_WIN 1 // Request window-local option value +#define SREQ_BUF 2 // Request buffer-local option value /* * Default values for 'errorformat'. @@ -476,8 +476,9 @@ EXTERN char_u *p_isp; // 'isprint' EXTERN int p_js; // 'joinspaces' EXTERN char_u *p_kp; // 'keywordprg' EXTERN char_u *p_km; // 'keymodel' -EXTERN char_u *p_langmap; // 'langmap'*/ -EXTERN int p_lnr; // 'langnoremap'*/ +EXTERN char_u *p_langmap; // 'langmap' +EXTERN int p_lnr; // 'langnoremap' +EXTERN int p_lrm; // 'langremap' EXTERN char_u *p_lm; // 'langmenu' EXTERN char_u *p_lispwords; // 'lispwords' EXTERN long p_ls; // 'laststatus' @@ -524,6 +525,7 @@ EXTERN int p_ru; // 'ruler' EXTERN char_u *p_ruf; // 'rulerformat' EXTERN char_u *p_pp; // 'packpath' EXTERN char_u *p_rtp; // 'runtimepath' +EXTERN long p_scbk; // 'scrollback' EXTERN long p_sj; // 'scrolljump' EXTERN long p_so; // 'scrolloff' EXTERN char_u *p_sbo; // 'scrollopt' @@ -811,4 +813,6 @@ enum { /* Value for b_p_ul indicating the global value must be used. */ #define NO_LOCAL_UNDOLEVEL -123456 +#define SB_MAX 100000 // Maximum 'scrollback' value. + #endif // NVIM_OPTION_DEFS_H diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 853c2b52d7..4ca63f2efe 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1000,7 +1000,7 @@ return { deny_duplicates=true, vi_def=true, varname='p_guicursor', - defaults={if_true={vi="n-v-c:block,o:hor50,i-ci:hor15,r-cr:hor30,sm:block"}} + defaults={if_true={vi="n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175"}} }, { full_name='guifont', abbreviation='gfn', @@ -1026,13 +1026,6 @@ return { enable_if=false, }, { - full_name='guiheadroom', abbreviation='ghr', - type='number', scope={'global'}, - vi_def=true, - enable_if=false, - defaults={if_true={vi=50}} - }, - { full_name='guioptions', abbreviation='go', type='string', list='flags', scope={'global'}, vi_def=true, @@ -1347,6 +1340,12 @@ return { defaults={if_true={vi=false, vim=true}} }, { + full_name='langremap', abbreviation='lrm', + type='bool', scope={'global'}, + varname='p_lrm', + defaults={if_true={vi=true, vim=false}} + }, + { full_name='laststatus', abbreviation='ls', type='number', scope={'global'}, vim=true, @@ -1918,7 +1917,7 @@ return { vi_def=true, varname='p_scbk', redraw={'current_buffer'}, - defaults={if_true={vi=-1}} + defaults={if_true={vi=1000}} }, { full_name='scrollbind', abbreviation='scb', diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index a73d753e46..839e0d1b51 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -615,9 +615,9 @@ char *vim_getenv(const char *name) vim_path = (char *)p_hf; } + char exe_name[MAXPATHL]; // Find runtime path relative to the nvim binary: ../share/nvim/runtime if (vim_path == NULL) { - char exe_name[MAXPATHL]; size_t exe_name_len = MAXPATHL; if (os_exepath(exe_name, &exe_name_len) == 0) { char *path_end = (char *)path_tail_with_sep((char_u *)exe_name); @@ -703,7 +703,8 @@ char *vim_getenv(const char *name) /// @param dstlen Maximum length of the result /// @param one If true, only replace one file name, including spaces and commas /// in the file name -void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, bool one) +void home_replace(const buf_T *const buf, const char_u *src, + char_u *dst, size_t dstlen, bool one) { size_t dirlen = 0, envlen = 0; size_t len; @@ -717,7 +718,7 @@ void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, bool one) * If the file is a help file, remove the path completely. */ if (buf != NULL && buf->b_help) { - STRCPY(dst, path_tail(src)); + xstrlcpy((char *)dst, (char *)path_tail(src), dstlen); return; } @@ -809,7 +810,7 @@ char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET len += STRLEN(src); } char_u *dst = xmalloc(len); - home_replace(buf, src, dst, (int)len, true); + home_replace(buf, src, dst, len, true); return dst; } @@ -888,3 +889,17 @@ bool os_setenv_append_path(const char *fname) } return false; } + +/// Returns true if the terminal can be assumed to silently ignore unknown +/// control codes. +bool os_term_is_nice(void) +{ +#if defined(__APPLE__) || defined(WIN32) + return true; +#else + const char *vte_version = os_getenv("VTE_VERSION"); + return (vte_version && atoi(vte_version) >= 3900) + || NULL != os_getenv("KONSOLE_PROFILE_NAME") + || NULL != os_getenv("KONSOLE_DBUS_SESSION"); +#endif +} diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 775f2bd449..4b7b53fc7f 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -113,27 +113,31 @@ FileDescriptor *file_open_new(int *const error, const char *const fname, /// Close file and free its buffer /// /// @param[in,out] fp File to close. +/// @param[in] do_fsync If true, use fsync() to write changes to disk. /// /// @return 0 or error code. -int file_close(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL +int file_close(FileDescriptor *const fp, const bool do_fsync) + FUNC_ATTR_NONNULL_ALL { - const int error = file_fsync(fp); - const int error2 = os_close(fp->fd); + const int flush_error = (do_fsync ? file_fsync(fp) : file_flush(fp)); + const int close_error = os_close(fp->fd); rbuffer_free(fp->rv); - if (error2 != 0) { - return error2; + if (close_error != 0) { + return close_error; } - return error; + return flush_error; } /// Close and free file obtained using file_open_new() /// /// @param[in,out] fp File to close. +/// @param[in] do_fsync If true, use fsync() to write changes to disk. /// /// @return 0 or error code. -int file_free(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL +int file_free(FileDescriptor *const fp, const bool do_fsync) + FUNC_ATTR_NONNULL_ALL { - const int ret = file_close(fp); + const int ret = file_close(fp, do_fsync); xfree(fp); return ret; } @@ -143,21 +147,36 @@ int file_free(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL /// @param[in,out] fp File to work with. /// /// @return 0 or error code. -int file_fsync(FileDescriptor *const fp) +int file_flush(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL { if (!fp->wr) { return 0; } file_rb_write_full_cb(fp->rv, fp); - if (fp->_error != 0) { - const int error = fp->_error; - fp->_error = 0; - return error; + const int error = fp->_error; + fp->_error = 0; + return error; +} + +/// Flush file modifications to disk and run fsync() +/// +/// @param[in,out] fp File to work with. +/// +/// @return 0 or error code. +int file_fsync(FileDescriptor *const fp) + FUNC_ATTR_NONNULL_ALL +{ + if (!fp->wr) { + return 0; } - const int error = os_fsync(fp->fd); - if (error != UV_EINVAL && error != UV_EROFS) { - return error; + const int flush_error = file_flush(fp); + if (flush_error != 0) { + return flush_error; + } + const int fsync_error = os_fsync(fp->fd); + if (fsync_error != UV_EINVAL && fsync_error != UV_EROFS) { + return fsync_error; } return 0; } diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 2beeae7ec6..3833a43f5f 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -91,11 +91,11 @@ int os_dirname(char_u *buf, size_t len) /// Check if the given path is a directory and not a symlink to a directory. /// @return `true` if `name` is a directory and NOT a symlink to a directory. /// `false` if `name` is not a directory or if an error occurred. -bool os_isrealdir(const char_u *name) +bool os_isrealdir(const char *name) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - if (uv_fs_lstat(&fs_loop, &request, (char *)name, NULL) != kLibuvSuccess) { + if (uv_fs_lstat(&fs_loop, &request, name, NULL) != kLibuvSuccess) { return false; } if (S_ISLNK(request.statbuf.st_mode)) { @@ -111,7 +111,7 @@ bool os_isrealdir(const char_u *name) bool os_isdir(const char_u *name) FUNC_ATTR_NONNULL_ALL { - int32_t mode = os_getperm(name); + int32_t mode = os_getperm((const char *)name); if (mode < 0) { return false; } @@ -236,7 +236,8 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) pathext); #else // Must have path separator, cannot execute files in the current directory. - bool ok = gettail_dir(name) != name && is_executable((char *)name); + const bool ok = ((const char_u *)gettail_dir((const char *)name) != name + && is_executable((char *)name)); #endif if (ok) { if (abspath != NULL) { @@ -254,7 +255,7 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) static bool is_executable(const char *name) FUNC_ATTR_NONNULL_ALL { - int32_t mode = os_getperm((char_u *)name); + int32_t mode = os_getperm((const char *)name); if (mode < 0) { return false; @@ -606,11 +607,11 @@ static int os_stat(const char *name, uv_stat_t *statbuf) /// Get the file permissions for a given file. /// /// @return libuv error code on error. -int32_t os_getperm(const char_u *name) +int32_t os_getperm(const char *name) FUNC_ATTR_NONNULL_ALL { uv_stat_t statbuf; - int stat_result = os_stat((char *)name, &statbuf); + int stat_result = os_stat(name, &statbuf); if (stat_result == kLibuvSuccess) { return (int32_t)statbuf.st_mode; } else { @@ -621,11 +622,11 @@ int32_t os_getperm(const char_u *name) /// Set the permission of a file. /// /// @return `OK` for success, `FAIL` for failure. -int os_setperm(const char_u *name, int perm) +int os_setperm(const char *const name, int perm) FUNC_ATTR_NONNULL_ALL { int r; - RUN_UV_FS_FUNC(r, uv_fs_chmod, (const char *)name, perm, NULL); + RUN_UV_FS_FUNC(r, uv_fs_chmod, name, perm, NULL); return (r == kLibuvSuccess ? OK : FAIL); } @@ -979,13 +980,13 @@ bool os_fileid_equal_fileinfo(const FileID *file_id, /// When "fname" is the name of a shortcut (*.lnk) resolve the file it points /// to and return that name in allocated memory. /// Otherwise NULL is returned. -char *os_resolve_shortcut(char_u *fname) +char *os_resolve_shortcut(const char *fname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { HRESULT hr; IPersistFile *ppf = NULL; OLECHAR wsz[MAX_PATH]; char *rfname = NULL; - int len; IShellLinkW *pslw = NULL; WIN32_FIND_DATAW ffdw; @@ -994,7 +995,7 @@ char *os_resolve_shortcut(char_u *fname) if (fname == NULL) { return rfname; } - len = (int)STRLEN(fname); + const size_t len = strlen(fname); if (len <= 4 || STRNICMP(fname + len - 4, ".lnk", 4) != 0) { return rfname; } @@ -1006,7 +1007,7 @@ char *os_resolve_shortcut(char_u *fname) &IID_IShellLinkW, (void **)&pslw); if (hr == S_OK) { WCHAR *p; - int conversion_result = utf8_to_utf16((char *)fname, &p); + const int conversion_result = utf8_to_utf16(fname, &p); if (conversion_result != 0) { EMSG2("utf8_to_utf16 failed: %s", uv_strerror(conversion_result)); } @@ -1036,7 +1037,7 @@ char *os_resolve_shortcut(char_u *fname) ZeroMemory(wsz, MAX_PATH * sizeof(WCHAR)); hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0); if (hr == S_OK && wsz[0] != NUL) { - int conversion_result = utf16_to_utf8(wsz, &rfname); + const int conversion_result = utf16_to_utf8(wsz, &rfname); if (conversion_result != 0) { EMSG2("utf16_to_utf8 failed: %s", uv_strerror(conversion_result)); } diff --git a/src/nvim/os/fs_defs.h b/src/nvim/os/fs_defs.h index 0bd9c37750..2277d926b3 100644 --- a/src/nvim/os/fs_defs.h +++ b/src/nvim/os/fs_defs.h @@ -14,7 +14,7 @@ typedef struct { uint64_t device_id; ///< @private The id of the device containing the file } FileID; -#define FILE_ID_EMPTY (FileID) {.inode = 0, .device_id = 0} +#define FILE_ID_EMPTY (FileID) { .inode = 0, .device_id = 0 } typedef struct { uv_fs_t request; ///< @private The request to uv for the directory. diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index 14c210c69c..f81785675e 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -27,11 +27,11 @@ // Use up to 5 Mbyte for a buffer. #ifndef DFLT_MAXMEM -# define DFLT_MAXMEM (5*1024) +# define DFLT_MAXMEM (5 * 1024) #endif // use up to 10 Mbyte for Vim. #ifndef DFLT_MAXMEMTOT -# define DFLT_MAXMEMTOT (10*1024) +# define DFLT_MAXMEMTOT (10 * 1024) #endif // Note: Some systems need both string.h and strings.h (Savage). However, diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 20cc589925..8e2b37a1c1 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -12,8 +12,9 @@ typedef struct pty_process { #define pty_process_spawn(job) libuv_process_spawn((LibuvProcess *)job) #define pty_process_close(job) libuv_process_close((LibuvProcess *)job) #define pty_process_close_master(job) libuv_process_close((LibuvProcess *)job) -#define pty_process_resize(job, width, height) -#define pty_process_teardown(loop) +#define pty_process_resize(job, width, height) ( \ + (void)job, (void)width, (void)height, 0) +#define pty_process_teardown(loop) ((void)loop, 0) static inline PtyProcess pty_process_init(Loop *loop, void *data) { diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h index c98aa88bfa..5c9daca476 100644 --- a/src/nvim/os/unix_defs.h +++ b/src/nvim/os/unix_defs.h @@ -8,7 +8,7 @@ // POSIX.1-2008 says that NAME_MAX should be in here #include <limits.h> -#define TEMP_DIR_NAMES {"$TMPDIR", "/tmp", ".", "~"} +#define TEMP_DIR_NAMES { "$TMPDIR", "/tmp", ".", "~" } #define TEMP_FILE_PATH_MAXLEN 256 #define HAVE_ACL (HAVE_POSIX_ACL || HAVE_SOLARIS_ACL) diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index 8de896c490..7c980c3768 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -1,6 +1,10 @@ #ifndef NVIM_OS_WIN_DEFS_H #define NVIM_OS_WIN_DEFS_H +#ifndef WIN32 +# error Header must be included only when compiling for Windows. +#endif + // winsock2.h must be first to avoid incompatibilities // with winsock.h (included by windows.h) #include <winsock2.h> @@ -15,7 +19,7 @@ #define NAME_MAX _MAX_PATH -#define TEMP_DIR_NAMES {"$TMP", "$TEMP", "$USERPROFILE", ""} +#define TEMP_DIR_NAMES { "$TMPDIR", "$TMP", "$TEMP", "$USERPROFILE", "" } #define TEMP_FILE_PATH_MAXLEN _MAX_PATH #define FNAME_ILLEGAL "\"*?><|" diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index ed3410ffe5..acd86f06dc 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -110,14 +110,14 @@ void mch_copy_sec(char_u *from_file, char_u *to_file) // Return a pointer to the ACL of file "fname" in allocated memory. // Return NULL if the ACL is not available for whatever reason. -vim_acl_T mch_get_acl(char_u *fname) +vim_acl_T mch_get_acl(const char_u *fname) { vim_acl_T ret = NULL; return ret; } // Set the ACL of file "fname" to "acl" (unless it's NULL). -void mch_set_acl(char_u *fname, vim_acl_T aclent) +void mch_set_acl(const char_u *fname, vim_acl_T aclent) { if (aclent == NULL) return; diff --git a/src/nvim/path.c b/src/nvim/path.c index dfcafc85de..6bf42ed2fa 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -84,15 +84,15 @@ FileComparison path_full_compare(char_u *s1, char_u *s2, int checkname) /// /// @return pointer just past the last path separator (empty string, if fname /// ends in a slash), or empty string if fname is NULL. -char_u *path_tail(char_u *fname) +char_u *path_tail(const char_u *fname) FUNC_ATTR_NONNULL_RET { if (fname == NULL) { return (char_u *)""; } - char_u *tail = get_past_head(fname); - char_u *p = tail; + const char_u *tail = get_past_head(fname); + const char_u *p = tail; // Find last part of path. while (*p != NUL) { if (vim_ispathsep_nocolon(*p)) { @@ -100,7 +100,7 @@ char_u *path_tail(char_u *fname) } mb_ptr_adv(p); } - return tail; + return (char_u *)tail; } /// Get pointer to tail of "fname", including path separators. @@ -159,7 +159,7 @@ const char_u *invocation_path_tail(const char_u *invocation, size_t *len) /// @param fname A file path. (Must be != NULL.) /// @return Pointer to first found path separator + 1. /// An empty string, if `fname` doesn't contain a path separator, -char_u *path_next_component(char_u *fname) +const char *path_next_component(const char *fname) { assert(fname != NULL); while (*fname != NUL && !vim_ispathsep(*fname)) { @@ -174,9 +174,9 @@ char_u *path_next_component(char_u *fname) /// Get a pointer to one character past the head of a path name. /// Unix: after "/"; Win: after "c:\" /// If there is no head, path is returned. -char_u *get_past_head(char_u *path) +char_u *get_past_head(const char_u *path) { - char_u *retval = path; + const char_u *retval = path; #ifdef WIN32 // May skip "c:" @@ -189,7 +189,7 @@ char_u *get_past_head(char_u *path) ++retval; } - return retval; + return (char_u *)retval; } /* @@ -282,48 +282,63 @@ bool dir_of_file_exists(char_u *fname) return retval; } -/* - * Versions of fnamecmp() and fnamencmp() that handle '/' and '\' equally - * and deal with 'fileignorecase'. - */ -int vim_fnamecmp(char_u *x, char_u *y) +/// Compare two file names +/// +/// Handles '/' and '\\' correctly and deals with &fileignorecase option. +/// +/// @param[in] fname1 First file name. +/// @param[in] fname2 Second file name. +/// +/// @return 0 if they are equal, non-zero otherwise. +int path_fnamecmp(const char *fname1, const char *fname2) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { #ifdef BACKSLASH_IN_FILENAME - return vim_fnamencmp(x, y, MAXPATHL); + const size_t len1 = strlen(fname1); + const size_t len2 = strlen(fname2); + return path_fnamencmp(fname1, fname2, MAX(len1, len2)); #else - if (p_fic) - return mb_stricmp(x, y); - return STRCMP(x, y); + return mb_strcmp_ic((bool)p_fic, fname1, fname2); #endif } -int vim_fnamencmp(char_u *x, char_u *y, size_t len) +/// Compare two file names +/// +/// Handles '/' and '\\' correctly and deals with &fileignorecase option. +/// +/// @param[in] fname1 First file name. +/// @param[in] fname2 Second file name. +/// @param[in] len Compare at most len bytes. +/// +/// @return 0 if they are equal, non-zero otherwise. +int path_fnamencmp(const char *const fname1, const char *const fname2, + size_t len) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { #ifdef BACKSLASH_IN_FILENAME - char_u *px = x; - char_u *py = y; - int cx = NUL; - int cy = NUL; + int c1 = NUL; + int c2 = NUL; + const char *p1 = fname1; + const char *p2 = fname2; while (len > 0) { - cx = PTR2CHAR(px); - cy = PTR2CHAR(py); - if (cx == NUL || cy == NUL - || ((p_fic ? vim_tolower(cx) != vim_tolower(cy) : cx != cy) - && !(cx == '/' && cy == '\\') - && !(cx == '\\' && cy == '/'))) + c1 = PTR2CHAR((const char_u *)p1); + c2 = PTR2CHAR((const char_u *)p2); + if ((c1 == NUL || c2 == NUL + || (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/')))) + && (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) { break; - len -= MB_PTR2LEN(px); - px += MB_PTR2LEN(px); - py += MB_PTR2LEN(py); + } + len -= MB_PTR2LEN((const char_u *)p1); + p1 += MB_PTR2LEN((const char_u *)p1); + p2 += MB_PTR2LEN((const char_u *)p2); } - if (len == 0) - return 0; - return cx - cy; + return c1 - c2; #else - if (p_fic) - return mb_strnicmp(x, y, len); - return STRNCMP(x, y, len); + if (p_fic) { + return mb_strnicmp((const char_u *)fname1, (const char_u *)fname2, len); + } + return strncmp(fname1, fname2, len); #endif } @@ -416,7 +431,7 @@ bool add_pathsep(char *p) /// /// @return [allocated] Copy of absolute path to `fname` or NULL when /// `fname` is NULL. -char *FullName_save(char *fname, bool force) +char *FullName_save(const char *fname, bool force) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC { if (fname == NULL) { @@ -804,7 +819,7 @@ static void expand_path_option(char_u *curdir, garray_T *gap) } STRMOVE(buf + len + 1, buf); STRCPY(buf, curdir); - buf[len] = PATHSEP; + buf[len] = (char_u)PATHSEP; simplify_filename(buf); } @@ -891,9 +906,9 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char_u *)); for (int i = 0; i < gap->ga_len && !got_int; i++) { - char_u *path = fnames[i]; + char_u *path = fnames[i]; int is_in_curdir; - char_u *dir_end = gettail_dir(path); + char_u *dir_end = (char_u *)gettail_dir((const char *)path); char_u *pathsep_p; char_u *path_cutoff; @@ -995,18 +1010,22 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) ga_remove_duplicate_strings(gap); } -/// Return the end of the directory name, on the first path -/// separator: -/// "/path/file", "/path/dir/", "/path//dir", "/file" -/// ^ ^ ^ ^ -char_u *gettail_dir(const char_u *fname) +/// Find end of the directory name +/// +/// @param[in] fname File name to process. +/// +/// @return end of the directory name, on the first path separator: +/// +/// "/path/file", "/path/dir/", "/path//dir", "/file" +/// ^ ^ ^ ^ +const char *gettail_dir(const char *const fname) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - const char_u *dir_end = fname; - const char_u *next_dir_end = fname; + const char *dir_end = fname; + const char *next_dir_end = fname; bool look_for_sep = true; - const char_u *p; - for (p = fname; *p != NUL; ) { + for (const char *p = fname; *p != NUL; ) { if (vim_ispathsep(*p)) { if (look_for_sep) { next_dir_end = p; @@ -1019,7 +1038,7 @@ char_u *gettail_dir(const char_u *fname) } mb_ptr_adv(p); } - return (char_u *)dir_end; + return dir_end; } @@ -1314,12 +1333,12 @@ static int expand_backtick( /// When the path looks like a URL leave it unmodified. void slash_adjust(char_u *p) { - if (path_with_url(p)) { + if (path_with_url((const char *)p)) { return; } while (*p) { - if (*p == psepcN) { - *p = psepc; + if (*p == (char_u)psepcN) { + *p = (char_u)psepc; } mb_ptr_adv(p); } @@ -1538,8 +1557,8 @@ void simplify_filename(char_u *filename) p = tail; /* skip to char after ".." or "../" */ } } else { - ++components; /* simple path component */ - p = path_next_component(p); + components++; // Simple path component. + p = (char_u *)path_next_component((const char *)p); } } while (*p != NUL); } diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index d2b62f89a0..121f22129a 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -32,10 +32,10 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) zh_CN.UTF-8 zh_TW.UTF-8) - set(NEOVIM_RELATIVE_SOURCES) - foreach(SRC ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) + set(NVIM_RELATIVE_SOURCES) + foreach(SRC ${NVIM_SOURCES} ${NVIM_HEADERS}) file(RELATIVE_PATH RELATIVE_SRC ${CMAKE_CURRENT_SOURCE_DIR} ${SRC}) - list(APPEND NEOVIM_RELATIVE_SOURCES ${RELATIVE_SRC}) + list(APPEND NVIM_RELATIVE_SOURCES ${RELATIVE_SRC}) endforeach() set(NVIM_POT ${CMAKE_CURRENT_BINARY_DIR}/nvim.pot) @@ -46,9 +46,9 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) -DXGETTEXT_PRG=${XGETTEXT_PRG} -DPOT_FILE=${NVIM_POT} -DSEARCH_DIR=${CMAKE_CURRENT_SOURCE_DIR} - "\"-DSOURCES=${NEOVIM_RELATIVE_SOURCES}\"" + "\"-DSOURCES=${NVIM_RELATIVE_SOURCES}\"" -P ${PROJECT_SOURCE_DIR}/cmake/RunXgettext.cmake - DEPENDS ${NEOVIM_SOURCES}) + DEPENDS ${NVIM_SOURCES}) add_custom_target(potfile DEPENDS ${NVIM_POT}) diff --git a/src/nvim/po/check.vim b/src/nvim/po/check.vim index 1622741da6..b323174550 100644 --- a/src/nvim/po/check.vim +++ b/src/nvim/po/check.vim @@ -33,36 +33,66 @@ func! GetMline() return substitute(idline, '[^%]*\(%[-+ #''.0-9*]*l\=[dsuxXpoc%]\)\=', '\1', 'g') endfunc -" This only works when 'wrapscan' is set. +" This only works when 'wrapscan' is not set. let s:save_wrapscan = &wrapscan -set wrapscan +set nowrapscan " Start at the first "msgid" line. 1 -/^msgid -let startline = line('.') +/^msgid\> + +" When an error is detected this is set to the line number. +" Note: this is used in the Makefile. let error = 0 while 1 if getline(line('.') - 1) !~ "no-c-format" - let fromline = GetMline() + " go over the "msgid" and "msgid_plural" lines + let prevfromline = 'foobar' + while 1 + let fromline = GetMline() + if prevfromline != 'foobar' && prevfromline != fromline + echomsg 'Mismatching % in line ' . (line('.') - 1) + echomsg 'msgid: ' . prevfromline + echomsg 'msgid ' . fromline + if error == 0 + let error = line('.') + endif + endif + if getline('.') !~ 'msgid_plural' + break + endif + let prevfromline = fromline + endwhile + if getline('.') !~ '^msgstr' - echo 'Missing "msgstr" in line ' . line('.') - let error = 1 - endif - let toline = GetMline() - if fromline != toline - echo 'Mismatching % in line ' . (line('.') - 1) - echo 'msgid: ' . fromline - echo 'msgstr: ' . toline - let error = 1 + echomsg 'Missing "msgstr" in line ' . line('.') + if error == 0 + let error = line('.') + endif endif + + " check all the 'msgstr' lines + while getline('.') =~ '^msgstr' + let toline = GetMline() + if fromline != toline + echomsg 'Mismatching % in line ' . (line('.') - 1) + echomsg 'msgid: ' . fromline + echomsg 'msgstr: ' . toline + if error == 0 + let error = line('.') + endif + endif + if line('.') == line('$') + break + endif + endwhile endif - " Find next msgid. - " Wrap around at the end of the file, quit when back at the first one. - /^msgid - if line('.') == startline + " Find next msgid. Quit when there is no more. + let lnum = line('.') + silent! /^msgid\> + if line('.') == lnum break endif endwhile @@ -77,12 +107,16 @@ endwhile " 1 if search('msgid "\("\n"\)\?\([EW][0-9]\+:\).*\nmsgstr "\("\n"\)\?[^"]\@=\2\@!') > 0 - echo 'Mismatching error/warning code in line ' . line('.') - let error = 1 + echomsg 'Mismatching error/warning code in line ' . line('.') + if error == 0 + let error = line('.') + endif endif if error == 0 - echo "OK" + echomsg "OK" +else + exe error endif redir END diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index ea00afbd86..6346951c05 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -610,13 +610,10 @@ static int pum_set_selected(int n, int repeat) if (res == OK) { // Edit a new, empty buffer. Set options for a "wipeout" // buffer. - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, - (char_u *)"nofile", OPT_LOCAL); - set_option_value((char_u *)"bh", 0L, - (char_u *)"wipe", OPT_LOCAL); - set_option_value((char_u *)"diff", 0L, - NULL, OPT_LOCAL); + set_option_value("swf", 0L, NULL, OPT_LOCAL); + set_option_value("bt", 0L, "nofile", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); + set_option_value("diff", 0L, NULL, OPT_LOCAL); } } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 3f7975051f..4fa5c85abd 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1236,7 +1236,7 @@ static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum, qfp->qf_nr = nr; if (type != 1 && !vim_isprintc(type)) /* only printable chars allowed */ type = 0; - qfp->qf_type = type; + qfp->qf_type = (char_u)type; qfp->qf_valid = valid; lastp = &qi->qf_lists[qi->qf_curlist].qf_last; @@ -1975,7 +1975,7 @@ win_found: ok = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit); - if (qi != &ql_info && !win_valid(oldwin)) { + if (qi != &ql_info && !win_valid_any_tab(oldwin)) { EMSG(_("E924: Current window was closed")); is_abort = true; opened_window = false; @@ -2581,15 +2581,13 @@ void ex_copen(exarg_T *eap) else { /* Create a new quickfix buffer */ (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, oldwin); - /* switch off 'swapfile' */ - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, (char_u *)"quickfix", - OPT_LOCAL); - set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL); + // Switch off 'swapfile'. + set_option_value("swf", 0L, NULL, OPT_LOCAL); + set_option_value("bt", 0L, "quickfix", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); RESET_BINDING(curwin); - curwin->w_p_diff = FALSE; - set_option_value((char_u *)"fdm", 0L, (char_u *)"manual", - OPT_LOCAL); + curwin->w_p_diff = false; + set_option_value("fdm", 0L, "manual", OPT_LOCAL); } /* Only set the height when still in the same tab page and there is no @@ -2901,14 +2899,14 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last) } } - /* correct cursor position */ - check_lnums(TRUE); + // Correct cursor position. + check_lnums(true); if (old_last == NULL) { // Set the 'filetype' to "qf" each time after filling the buffer. This // resembles reading a file into a buffer, it's more logical when using // autocommands. - set_option_value((char_u *)"ft", 0L, (char_u *)"qf", OPT_LOCAL); + set_option_value("ft", 0L, "qf", OPT_LOCAL); curbuf->b_p_ma = false; keep_filetype = true; // don't detect 'filetype' @@ -3974,7 +3972,6 @@ static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start) int get_errorlist(win_T *wp, int qf_idx, list_T *list) { qf_info_T *qi = &ql_info; - dict_T *dict; char_u buf[2]; qfline_T *qfp; int i; @@ -4002,23 +3999,34 @@ int get_errorlist(win_T *wp, int qf_idx, list_T *list) if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) bufnum = 0; - dict = dict_alloc(); - list_append_dict(list, dict); + dict_T *const dict = tv_dict_alloc(); + tv_list_append_dict(list, dict); buf[0] = qfp->qf_type; buf[1] = NUL; - if ( dict_add_nr_str(dict, "bufnr", (long)bufnum, NULL) == FAIL - || dict_add_nr_str(dict, "lnum", (long)qfp->qf_lnum, NULL) == FAIL - || dict_add_nr_str(dict, "col", (long)qfp->qf_col, NULL) == FAIL - || dict_add_nr_str(dict, "vcol", (long)qfp->qf_viscol, NULL) == FAIL - || dict_add_nr_str(dict, "nr", (long)qfp->qf_nr, NULL) == FAIL - || dict_add_nr_str(dict, "pattern", 0L, - qfp->qf_pattern == NULL ? (char_u *)"" : qfp->qf_pattern) == FAIL - || dict_add_nr_str(dict, "text", 0L, - qfp->qf_text == NULL ? (char_u *)"" : qfp->qf_text) == FAIL - || dict_add_nr_str(dict, "type", 0L, buf) == FAIL - || dict_add_nr_str(dict, "valid", (long)qfp->qf_valid, NULL) == FAIL) - return FAIL; + if (tv_dict_add_nr(dict, S_LEN("bufnr"), (varnumber_T)bufnum) == FAIL + || (tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)qfp->qf_lnum) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)qfp->qf_col) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("vcol"), (varnumber_T)qfp->qf_viscol) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("nr"), (varnumber_T)qfp->qf_nr) == FAIL) + || tv_dict_add_str(dict, S_LEN("pattern"), + (qfp->qf_pattern == NULL + ? "" + : (const char *)qfp->qf_pattern)) == FAIL + || tv_dict_add_str(dict, S_LEN("text"), + (qfp->qf_text == NULL + ? "" + : (const char *)qfp->qf_text)) == FAIL + || tv_dict_add_str(dict, S_LEN("type"), (const char *)buf) == FAIL + || (tv_dict_add_nr(dict, S_LEN("valid"), (varnumber_T)qfp->qf_valid) + == FAIL)) { + // tv_dict_add* fail only if key already exist, but this is a newly + // allocated dictionary which is thus guaranteed to have no existing keys. + assert(false); + } qfp = qfp->qf_next; if (qfp == NULL) { @@ -4057,7 +4065,7 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) int flags = QF_GETLIST_NONE; int qf_idx = qi->qf_curlist; // default is the current list - if ((di = dict_find(what, (char_u *)"nr", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { qf_idx = di->di_tv.vval.v_number - 1; @@ -4070,15 +4078,15 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) } } - if (dict_find(what, (char_u *)"all", -1) != NULL) { + if (tv_dict_find(what, S_LEN("all")) != NULL) { flags |= QF_GETLIST_ALL; } - if (dict_find(what, (char_u *)"title", -1) != NULL) { + if (tv_dict_find(what, S_LEN("title")) != NULL) { flags |= QF_GETLIST_TITLE; } - if (dict_find(what, (char_u *)"winid", -1) != NULL) { + if (tv_dict_find(what, S_LEN("winid")) != NULL) { flags |= QF_GETLIST_WINID; } @@ -4087,15 +4095,15 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) if (t == NULL) { t = (char_u *)""; } - status = dict_add_nr_str(retdict, "title", 0L, t); + status = tv_dict_add_str(retdict, S_LEN("title"), (const char *)t); } if ((status == OK) && (flags & QF_GETLIST_NR)) { - status = dict_add_nr_str(retdict, "nr", qf_idx + 1, NULL); + status = tv_dict_add_nr(retdict, S_LEN("nr"), qf_idx + 1); } if ((status == OK) && (flags & QF_GETLIST_WINID)) { win_T *win = qf_find_win(qi); if (win != NULL) { - status = dict_add_nr_str(retdict, "winid", win->handle, NULL); + status = tv_dict_add_nr(retdict, S_LEN("winid"), win->handle); } } @@ -4132,17 +4140,18 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, if (d == NULL) continue; - char_u *filename = get_dict_string(d, "filename", true); - int bufnum = (int)get_dict_number(d, "bufnr"); - long lnum = get_dict_number(d, "lnum"); - int col = (int)get_dict_number(d, "col"); - char_u vcol = (char_u)get_dict_number(d, "vcol"); - int nr = (int)get_dict_number(d, "nr"); - char_u *type = get_dict_string(d, "type", true); - char_u *pattern = get_dict_string(d, "pattern", true); - char_u *text = get_dict_string(d, "text", true); + char *const filename = tv_dict_get_string(d, "filename", true); + int bufnum = (int)tv_dict_get_number(d, "bufnr"); + long lnum = tv_dict_get_number(d, "lnum"); + int col = (int)tv_dict_get_number(d, "col"); + char_u vcol = (char_u)tv_dict_get_number(d, "vcol"); + int nr = (int)tv_dict_get_number(d, "nr"); + const char *type_str = tv_dict_get_string(d, "type", false); + const char_u type = (char_u)(uint8_t)(type_str == NULL ? NUL : *type_str); + char *const pattern = tv_dict_get_string(d, "pattern", true); + char *text = tv_dict_get_string(d, "text", true); if (text == NULL) { - text = vim_strsave((char_u *)""); + text = xcalloc(1, 1); } bool valid = true; if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) { @@ -4162,21 +4171,20 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, int status = qf_add_entry(qi, NULL, // dir - filename, + (char_u *)filename, bufnum, - text, + (char_u *)text, lnum, col, vcol, // vis_col - pattern, // search pattern + (char_u *)pattern, // search pattern nr, - (char_u)(type == NULL ? NUL : *type), + type, valid); xfree(filename); xfree(pattern); xfree(text); - xfree(type); if (status == FAIL) { retval = FAIL; @@ -4213,7 +4221,7 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) newlist = true; } int qf_idx = qi->qf_curlist; // default is the current list - if ((di = dict_find(what, (char_u *)"nr", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { qf_idx = di->di_tv.vval.v_number - 1; @@ -4231,10 +4239,11 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) qf_idx = qi->qf_curlist; } - if ((di = dict_find(what, (char_u *)"title", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("title"))) != NULL) { if (di->di_tv.v_type == VAR_STRING) { xfree(qi->qf_lists[qf_idx].qf_title); - qi->qf_lists[qf_idx].qf_title = get_dict_string(what, "title", true); + qi->qf_lists[qf_idx].qf_title = (char_u *)tv_dict_get_string( + what, "title", true); if (qf_idx == qi->qf_curlist) { qf_update_win_titlevar(qi); } @@ -4363,7 +4372,6 @@ void ex_cbuffer(exarg_T *eap) */ void ex_cexpr(exarg_T *eap) { - typval_T *tv; qf_info_T *qi = &ql_info; const char *au_name = NULL; @@ -4403,11 +4411,11 @@ void ex_cexpr(exarg_T *eap) /* Evaluate the expression. When the result is a string or a list we can * use it to fill the errorlist. */ - tv = eval_expr(eap->arg, NULL); - if (tv != NULL) { - if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL) - || (tv->v_type == VAR_LIST && tv->vval.v_list != NULL)) { - if (qf_init_ext(qi, NULL, NULL, tv, p_efm, + typval_T tv; + if (eval0(eap->arg, &tv, NULL, true) != FAIL) { + if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL) + || (tv.v_type == VAR_LIST && tv.vval.v_list != NULL)) { + if (qf_init_ext(qi, NULL, NULL, &tv, p_efm, (eap->cmdidx != CMD_caddexpr && eap->cmdidx != CMD_laddexpr), (linenr_T)0, (linenr_T)0, *eap->cmdlinep) > 0) { @@ -4422,7 +4430,7 @@ void ex_cexpr(exarg_T *eap) } else { EMSG(_("E777: String or List expected")); } - free_tv(tv); + tv_clear(&tv); } } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 1cd334abcd..9baa53d2a2 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -2221,10 +2221,11 @@ collection: if (*regparse == '[') endc = get_coll_element(®parse); if (endc == 0) { - if (has_mbyte) - endc = mb_ptr2char_adv(®parse); - else + if (has_mbyte) { + endc = mb_ptr2char_adv((const char_u **)®parse); + } else { endc = *regparse++; + } } /* Handle \o40, \x20 and \u20AC style sequences */ @@ -3650,9 +3651,11 @@ static long regtry(bt_regprog_T *prog, colnr_T col) */ static int reg_prev_class(void) { - if (reginput > regline) - return mb_get_class_buf(reginput - 1 - - (*mb_head_off)(regline, reginput - 1), reg_buf); + if (reginput > regline) { + return mb_get_class_tab(reginput - 1 - (*mb_head_off)(regline, + reginput - 1), + reg_buf->b_chartab); + } return -1; } @@ -3918,12 +3921,13 @@ regmatch ( else if (has_mbyte) { int this_class; - /* Get class of current and previous char (if it exists). */ - this_class = mb_get_class_buf(reginput, reg_buf); - if (this_class <= 1) - status = RA_NOMATCH; /* not on a word at all */ - else if (reg_prev_class() == this_class) - status = RA_NOMATCH; /* previous char is in same word */ + // Get class of current and previous char (if it exists). + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); + if (this_class <= 1) { + status = RA_NOMATCH; // Not on a word at all. + } else if (reg_prev_class() == this_class) { + status = RA_NOMATCH; // Previous char is in same word. + } } else { if (!vim_iswordc_buf(c, reg_buf) || (reginput > regline && vim_iswordc_buf(reginput[-1 @@ -3938,8 +3942,8 @@ regmatch ( else if (has_mbyte) { int this_class, prev_class; - /* Get class of current and previous char (if it exists). */ - this_class = mb_get_class_buf(reginput, reg_buf); + // Get class of current and previous char (if it exists). + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); prev_class = reg_prev_class(); if (this_class == prev_class || prev_class == 0 || prev_class == 1) @@ -6268,8 +6272,8 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n) str2 = s2; c1 = c2 = 0; while ((int)(str1 - s1) < *n) { - c1 = mb_ptr2char_adv(&str1); - c2 = mb_ptr2char_adv(&str2); + c1 = mb_ptr2char_adv((const char_u **)&str1); + c2 = mb_ptr2char_adv((const char_u **)&str2); /* decompose the character if necessary, into 'base' characters * because I don't care about Arabic, I will hard-code the Hebrew @@ -6583,7 +6587,6 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, if (expr != NULL) { typval_T argv[2]; int dummy; - char_u buf[NUMBUFLEN]; typval_T rettv; staticList10_T matchList; @@ -6613,11 +6616,12 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, clear_submatch_list(&matchList); } } - eval_result = get_tv_string_buf_chk(&rettv, buf); + char buf[NUMBUFLEN]; + eval_result = (char_u *)tv_get_string_buf_chk(&rettv, buf); if (eval_result != NULL) { eval_result = vim_strsave(eval_result); } - clear_tv(&rettv); + tv_clear(&rettv); } else { eval_result = eval_to_string(source + 2, NULL, true); } @@ -6976,7 +6980,7 @@ list_T *reg_submatch_list(int no) linenr_T slnum; linenr_T elnum; list_T *list; - char_u *s; + const char *s; if (submatch_match == NULL) { slnum = submatch_mmatch->startpos[no].lnum; @@ -6988,27 +6992,27 @@ list_T *reg_submatch_list(int no) colnr_T scol = submatch_mmatch->startpos[no].col; colnr_T ecol = submatch_mmatch->endpos[no].col; - list = list_alloc(); + list = tv_list_alloc(); - s = reg_getline_submatch(slnum) + scol; + s = (const char *)reg_getline_submatch(slnum) + scol; if (slnum == elnum) { - list_append_string(list, s, ecol - scol); + tv_list_append_string(list, s, ecol - scol); } else { - list_append_string(list, s, -1); + tv_list_append_string(list, s, -1); for (int i = 1; i < elnum - slnum; i++) { - s = reg_getline_submatch(slnum + i); - list_append_string(list, s, -1); + s = (const char *)reg_getline_submatch(slnum + i); + tv_list_append_string(list, s, -1); } - s = reg_getline_submatch(elnum); - list_append_string(list, s, ecol); + s = (const char *)reg_getline_submatch(elnum); + tv_list_append_string(list, s, ecol); } } else { - s = submatch_match->startp[no]; + s = (const char *)submatch_match->startp[no]; if (s == NULL || submatch_match->endp[no] == NULL) { return NULL; } - list = list_alloc(); - list_append_string(list, s, (int)(submatch_match->endp[no] - s)); + list = tv_list_alloc(); + tv_list_append_string(list, s, (const char *)submatch_match->endp[no] - s); } return list; diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 474f3df32a..5b49ab38f0 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -3893,21 +3893,25 @@ state_in_list ( return FALSE; } -/* - * Add "state" and possibly what follows to state list ".". - * Returns "subs_arg", possibly copied into temp_subs. - */ +// Offset used for "off" by addstate_here(). +#define ADDSTATE_HERE_OFFSET 10 +// Add "state" and possibly what follows to state list ".". +// Returns "subs_arg", possibly copied into temp_subs. static regsubs_T * addstate ( nfa_list_T *l, /* runtime state list */ nfa_state_T *state, /* state to update */ regsubs_T *subs_arg, /* pointers to subexpressions */ nfa_pim_T *pim, /* postponed look-behind match */ - int off /* byte offset, when -1 go to next line */ -) + int off_arg) /* byte offset, when -1 go to next line */ { int subidx; + int off = off_arg; + int add_here = FALSE; + int listindex = 0; + int k; + int found = FALSE; nfa_thread_T *thread; lpos_T save_lpos; int save_in_use; @@ -3920,6 +3924,12 @@ addstate ( int did_print = FALSE; #endif + if (off_arg <= -ADDSTATE_HERE_OFFSET) { + add_here = true; + off = 0; + listindex = -(off_arg + ADDSTATE_HERE_OFFSET); + } + switch (state->c) { case NFA_NCLOSE: case NFA_MCLOSE: @@ -3996,13 +4006,28 @@ addstate ( * lower position is preferred. */ if (!nfa_has_backref && pim == NULL && !l->has_pim && state->c != NFA_MATCH) { + + /* When called from addstate_here() do insert before + * existing states. */ + if (add_here) { + for (k = 0; k < l->n && k < listindex; ++k) { + if (l->t[k].state->id == state->id) { + found = TRUE; + break; + } + } + } + + if (!add_here || found) { skip_add: #ifdef REGEXP_DEBUG - nfa_set_code(state->c); - fprintf(log_fd, "> Not adding state %d to list %d. char %d: %s\n", - abs(state->id), l->id, state->c, code); + nfa_set_code(state->c); + fprintf(log_fd, "> Not adding state %d to list %d. char %d: %s pim: %s has_pim: %d found: %d\n", + abs(state->id), l->id, state->c, code, + pim == NULL ? "NULL" : "yes", l->has_pim, found); #endif return subs; + } } /* Do not add the state again when it exists with the same @@ -4058,14 +4083,14 @@ skip_add: case NFA_SPLIT: /* order matters here */ - subs = addstate(l, state->out, subs, pim, off); - subs = addstate(l, state->out1, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); + subs = addstate(l, state->out1, subs, pim, off_arg); break; case NFA_EMPTY: case NFA_NOPEN: case NFA_NCLOSE: - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); break; case NFA_MOPEN: @@ -4145,7 +4170,7 @@ skip_add: sub->list.line[subidx].start = reginput + off; } - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); /* "subs" may have changed, need to set "sub" again */ if (state->c >= NFA_ZOPEN && state->c <= NFA_ZOPEN9) sub = &subs->synt; @@ -4168,7 +4193,7 @@ skip_add: ? subs->norm.list.multi[0].end_lnum >= 0 : subs->norm.list.line[0].end != NULL)) { /* Do not overwrite the position set by \ze. */ - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); break; } case NFA_MCLOSE1: @@ -4228,7 +4253,7 @@ skip_add: save_lpos.col = 0; } - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); /* "subs" may have changed, need to set "sub" again */ if (state->c >= NFA_ZCLOSE && state->c <= NFA_ZCLOSE9) sub = &subs->synt; @@ -4266,8 +4291,10 @@ addstate_here ( int count; int listidx = *ip; - /* first add the state(s) at the end, so that we know how many there are */ - addstate(l, state, subs, pim, 0); + /* First add the state(s) at the end, so that we know how many there are. + * Pass the listidx as offset (avoids adding another argument to + * addstate(). */ + addstate(l, state, subs, pim, -listidx - ADDSTATE_HERE_OFFSET); /* when "*ip" was at the end of the list, nothing to do */ if (listidx + 1 == tlen) @@ -5383,7 +5410,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, int this_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_buf(reginput, reg_buf); + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); if (this_class <= 1) { result = false; } else if (reg_prev_class() == this_class) { @@ -5408,7 +5435,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, int this_class, prev_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_buf(reginput, reg_buf); + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); prev_class = reg_prev_class(); if (this_class == prev_class || prev_class == 0 || prev_class == 1) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index b98e59ed06..d9a21aa81f 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2193,7 +2193,8 @@ win_line ( int prev_c1 = 0; /* first composing char for prev_c */ int did_line_attr = 0; - bool has_bufhl = false; // this buffer has highlight matches + bool search_attr_from_match = false; // if search_attr is from :match + bool has_bufhl = false; // this buffer has highlight matches int bufhl_attr = 0; // attributes desired by bufhl bufhl_lineinfo_T bufhl_info; // bufhl data for this line @@ -2625,6 +2626,7 @@ win_line ( if ((long)shl->startcol < v) { // match at leftcol shl->attr_cur = shl->attr; search_attr = shl->attr; + search_attr_from_match = shl != &search_hl; } area_highlighting = true; } @@ -2922,7 +2924,6 @@ win_line ( } } else if (v == (long)shl->endcol) { shl->attr_cur = 0; - prev_syntax_id = 0; next_search_hl(wp, shl, lnum, (colnr_T)v, shl == &search_hl ? NULL : cur); @@ -2963,6 +2964,7 @@ win_line ( /* Use attributes from match with highest priority among * 'search_hl' and the match list. */ + search_attr_from_match = false; search_attr = search_hl.attr_cur; cur = wp->w_match_head; shl_flag = FALSE; @@ -2975,8 +2977,10 @@ win_line ( shl_flag = TRUE; } else shl = &cur->hl; - if (shl->attr_cur != 0) + if (shl->attr_cur != 0) { search_attr = shl->attr_cur; + search_attr_from_match = shl != &search_hl; + } if (shl != &search_hl && cur != NULL) cur = cur->next; } @@ -3137,7 +3141,7 @@ win_line ( p_extra = extra; c = *p_extra; - mb_c = mb_ptr2char_adv(&p_extra); + mb_c = mb_ptr2char_adv((const char_u **)&p_extra); mb_utf8 = (c >= 0x80); n_extra = (int)STRLEN(p_extra); c_extra = NUL; @@ -3405,7 +3409,7 @@ win_line ( || (c == ' ' && lcs_space && ptr - line <= trailcol))) { c = (c == ' ') ? lcs_space : lcs_nbsp; n_attr = 1; - extra_attr = hl_attr(HLF_8); + extra_attr = hl_attr(HLF_0); saved_attr2 = char_attr; // save current attr mb_c = c; if (enc_utf8 && (*mb_char2len)(c) > 1) { @@ -3420,7 +3424,7 @@ win_line ( if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') { c = lcs_trail; n_attr = 1; - extra_attr = hl_attr(HLF_8); + extra_attr = hl_attr(HLF_0); saved_attr2 = char_attr; // save current attr mb_c = c; if (enc_utf8 && (*mb_char2len)(c) > 1) { @@ -3521,8 +3525,8 @@ win_line ( c_extra = lcs_tab2; } n_attr = tab_len + 1; - extra_attr = hl_attr(HLF_8); - saved_attr2 = char_attr; /* save current attr */ + extra_attr = hl_attr(HLF_0); + saved_attr2 = char_attr; // save current attr mb_c = c; if (enc_utf8 && (*mb_char2len)(c) > 1) { mb_utf8 = TRUE; @@ -3712,7 +3716,7 @@ win_line ( } // Don't override visual selection highlighting. - if (n_attr > 0 && draw_state == WL_LINE) { + if (n_attr > 0 && draw_state == WL_LINE && !search_attr_from_match) { char_attr = hl_combine_attr(char_attr, extra_attr); } @@ -6885,7 +6889,10 @@ static void draw_tabline(void) int use_sep_chars = (t_colors < 8 ); - redraw_tabline = FALSE; + if (ScreenLines == NULL) { + return; + } + redraw_tabline = false; if (tabline_height() < 1) diff --git a/src/nvim/search.c b/src/nvim/search.c index ba6c4e6548..c5c92b41c5 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -282,7 +282,7 @@ void restore_search_patterns(void) static inline void free_spat(struct spat *const spat) { xfree(spat->pat); - dict_unref(spat->additional_data); + tv_dict_unref(spat->additional_data); } #if defined(EXITFREE) @@ -356,9 +356,10 @@ int pat_has_uppercase(char_u *pat) return FALSE; } -char_u *last_csearch(void) +const char *last_csearch(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return lastc_bytes; + return (const char *)lastc_bytes; } int last_csearch_forward(void) @@ -1290,10 +1291,11 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat) * ignored because we are interested in the next line -- Acevedo */ if ((compl_cont_status & CONT_ADDING) && !(compl_cont_status & CONT_SOL)) { - if ((p_ic ? mb_stricmp(p, pat) : STRCMP(p, pat)) == 0) + if (mb_strcmp_ic((bool)p_ic, (const char *)p, (const char *)pat) == 0) { return OK; - } else if (*p != NUL) { /* ignore empty lines */ - /* expanding lines or words */ + } + } else if (*p != NUL) { // Ignore empty lines. + // Expanding lines or words. assert(compl_length >= 0); if ((p_ic ? mb_strnicmp(p, pat, (size_t)compl_length) : STRNCMP(p, pat, compl_length)) == 0) @@ -2417,32 +2419,20 @@ static int cls(void) int c; c = gchar_cursor(); - if (p_altkeymap && c == F_BLANK) - return 0; - if (c == ' ' || c == '\t' || c == NUL) + if (p_altkeymap && c == F_BLANK) { return 0; - if (enc_dbcs != 0 && c > 0xFF) { - /* If cls_bigword, report multi-byte chars as class 1. */ - if (enc_dbcs == DBCS_KOR && cls_bigword) - return 1; - - /* process code leading/trailing bytes */ - return dbcs_class(((unsigned)c >> 8), (c & 0xFF)); } - if (enc_utf8) { - c = utf_class(c); - if (c != 0 && cls_bigword) - return 1; - return c; + if (c == ' ' || c == '\t' || c == NUL) { + return 0; } - /* If cls_bigword is TRUE, report all non-blanks as class 1. */ - if (cls_bigword) - return 1; + c = utf_class(c); - if (vim_iswordc(c)) - return 2; - return 1; + // If cls_bigword is TRUE, report all non-blanks as class 1. + if (c != 0 && cls_bigword) { + return 1; + } + return c; } /* @@ -3028,7 +3018,8 @@ extend: ++curwin->w_cursor.col; VIsual = start_pos; VIsual_mode = 'v'; - redraw_curbuf_later(INVERTED); /* update the inversion */ + redraw_cmdline = true; // show mode later + redraw_curbuf_later(INVERTED); // update the inversion } else { /* include a newline after the sentence, if there is one */ if (incl(&curwin->w_cursor) == -1) diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c index 7670b64468..c72dafd08e 100644 --- a/src/nvim/sha256.c +++ b/src/nvim/sha256.c @@ -259,11 +259,11 @@ void sha256_finish(context_sha256_T *ctx, char_u digest[SHA256_SUM_SIZE]) /// /// @returns hex digest of "buf[buf_len]" in a static array. /// if "salt" is not NULL also do "salt[salt_len]". -char_u *sha256_bytes(const char_u *restrict buf, size_t buf_len, - const char_u *restrict salt, size_t salt_len) +const char *sha256_bytes(const uint8_t *restrict buf, size_t buf_len, + const uint8_t *restrict salt, size_t salt_len) { char_u sha256sum[SHA256_SUM_SIZE]; - static char_u hexit[SHA256_BUFFER_SIZE + 1]; // buf size + NULL + static char hexit[SHA256_BUFFER_SIZE + 1]; // buf size + NULL context_sha256_T ctx; sha256_self_test(); @@ -277,7 +277,7 @@ char_u *sha256_bytes(const char_u *restrict buf, size_t buf_len, sha256_finish(&ctx, sha256sum); for (size_t j = 0; j < SHA256_SUM_SIZE; j++) { - snprintf((char *) hexit + j * SHA_STEP, SHA_STEP+1, "%02x", sha256sum[j]); + snprintf(hexit + j * SHA_STEP, SHA_STEP + 1, "%02x", sha256sum[j]); } hexit[sizeof(hexit) - 1] = '\0'; return hexit; @@ -308,7 +308,7 @@ bool sha256_self_test(void) context_sha256_T ctx; char_u buf[1000]; char_u sha256sum[SHA256_SUM_SIZE]; - char_u *hexit; + const char *hexit; static bool sha256_self_tested = false; static bool failures = false; @@ -320,8 +320,8 @@ bool sha256_self_test(void) for (size_t i = 0; i < 3; i++) { if (i < 2) { - hexit = sha256_bytes((char_u *) sha_self_test_msg[i], - STRLEN(sha_self_test_msg[i]), + hexit = sha256_bytes((uint8_t *)sha_self_test_msg[i], + strlen(sha_self_test_msg[i]), NULL, 0); STRCPY(output, hexit); } else { diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 197b029591..c7b95958e0 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -30,7 +30,7 @@ #include "nvim/ex_getln.h" #include "nvim/search.h" #include "nvim/regexp.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/version.h" #include "nvim/path.h" #include "nvim/fileio.h" @@ -82,8 +82,6 @@ KHASH_SET_INIT_STR(strset) (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) #define convert_setup(vcp, from, to) \ (convert_setup(vcp, (char_u *)from, (char_u *)to)) -#define os_getperm(f) \ - (os_getperm((char_u *) f)) #define os_isdir(f) (os_isdir((char_u *) f)) #define regtilde(s, m) ((char *) regtilde((char_u *) s, m)) #define path_tail_with_sep(f) ((char *) path_tail_with_sep((char_u *)f)) @@ -813,7 +811,7 @@ static int open_shada_file_for_reading(const char *const fname, /// Wrapper for closing file descriptors static void close_file(void *cookie) { - const int error = file_free(cookie); + const int error = file_free(cookie, true); if (error != 0) { emsgf(_(SERR "System error while closing ShaDa file: %s"), os_strerror(error)); @@ -1223,7 +1221,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) khash_t(fnamebufs) fname_bufs = KHASH_EMPTY_TABLE(fnamebufs); khash_t(strset) oldfiles_set = KHASH_EMPTY_TABLE(strset); if (get_old_files && (oldfiles_list == NULL || force)) { - oldfiles_list = list_alloc(); + oldfiles_list = tv_list_alloc(); set_vim_var_list(VV_OLDFILES, oldfiles_list); } ShaDaReadResult srni_ret; @@ -1435,8 +1433,8 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) fname = xstrdup(fname); } int kh_ret; - (void) kh_put(strset, &oldfiles_set, fname, &kh_ret); - list_append_allocated_string(oldfiles_list, fname); + (void)kh_put(strset, &oldfiles_set, fname, &kh_ret); + tv_list_append_allocated_string(oldfiles_list, fname); if (!want_marks) { // Avoid free because this string was already used. cur_entry.data.filemark.fname = NULL; @@ -1573,7 +1571,9 @@ static char *shada_filename(const char *file) do { \ const String s_ = (s); \ msgpack_pack_str(spacker, s_.size); \ - msgpack_pack_str_body(spacker, s_.data, s_.size); \ + if (s_.size) { \ + msgpack_pack_str_body(spacker, s_.data, s_.size); \ + } \ } while (0) #define PACK_BIN(s) \ do { \ @@ -1621,10 +1621,10 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, for (const hashitem_T *hi= d->dv_hashtab.ht_array; todo; hi++) { \ if (!HASHITEM_EMPTY(hi)) { \ todo--; \ - dictitem_T *const di = HI2DI(hi); \ - const size_t key_len = strlen((const char *) hi->hi_key); \ + dictitem_T *const di = TV_DICT_HI2DI(hi); \ + const size_t key_len = strlen((const char *)hi->hi_key); \ msgpack_pack_str(spacker, key_len); \ - msgpack_pack_str_body(spacker, (const char *) hi->hi_key, key_len); \ + msgpack_pack_str_body(spacker, (const char *)hi->hi_key, key_len); \ if (encode_vim_to_msgpack(spacker, &di->di_tv, \ _("additional data of ShaDa " what)) \ == FAIL) { \ @@ -1965,7 +1965,7 @@ static ShaDaWriteResult shada_pack_encoded_entry(msgpack_packer *const packer, typval_T tgttv; var_item_copy(sd_conv, &entry.data.data.global_var.value, &tgttv, true, 0); - clear_tv(&entry.data.data.global_var.value); + tv_clear(&entry.data.data.global_var.value); entry.data.data.global_var.value = tgttv; } ret = shada_pack_entry(packer, entry.data, max_kbyte); @@ -2559,7 +2559,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, if (sd_writer->sd_conv.vc_type != CONV_NONE) { var_item_copy(&sd_writer->sd_conv, &vartv, &tgttv, true, 0); } else { - copy_tv(&vartv, &tgttv); + tv_copy(&vartv, &tgttv); } ShaDaWriteResult spe_ret; if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) { @@ -2573,13 +2573,13 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } } }, max_kbyte)) == kSDWriteFailed) { - clear_tv(&vartv); - clear_tv(&tgttv); + tv_clear(&vartv); + tv_clear(&tgttv); ret = kSDWriteFailed; goto shada_write_exit; } - clear_tv(&vartv); - clear_tv(&tgttv); + tv_clear(&vartv); + tv_clear(&tgttv); if (spe_ret == kSDWriteSuccessfull) { int kh_ret; (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret); @@ -3154,17 +3154,17 @@ static void shada_free_shada_entry(ShadaEntry *const entry) case kSDItemJump: case kSDItemGlobalMark: case kSDItemLocalMark: { - dict_unref(entry->data.filemark.additional_data); + tv_dict_unref(entry->data.filemark.additional_data); xfree(entry->data.filemark.fname); break; } case kSDItemSearchPattern: { - dict_unref(entry->data.search_pattern.additional_data); + tv_dict_unref(entry->data.search_pattern.additional_data); xfree(entry->data.search_pattern.pat); break; } case kSDItemRegister: { - dict_unref(entry->data.reg.additional_data); + tv_dict_unref(entry->data.reg.additional_data); for (size_t i = 0; i < entry->data.reg.contents_size; i++) { xfree(entry->data.reg.contents[i]); } @@ -3172,25 +3172,25 @@ static void shada_free_shada_entry(ShadaEntry *const entry) break; } case kSDItemHistoryEntry: { - list_unref(entry->data.history_item.additional_elements); + tv_list_unref(entry->data.history_item.additional_elements); xfree(entry->data.history_item.string); break; } case kSDItemVariable: { - list_unref(entry->data.global_var.additional_elements); + tv_list_unref(entry->data.global_var.additional_elements); xfree(entry->data.global_var.name); - clear_tv(&entry->data.global_var.value); + tv_clear(&entry->data.global_var.value); break; } case kSDItemSubString: { - list_unref(entry->data.sub_string.additional_elements); + tv_list_unref(entry->data.sub_string.additional_elements); xfree(entry->data.sub_string.sub); break; } case kSDItemBufferList: { for (size_t i = 0; i < entry->data.buffer_list.size; i++) { xfree(entry->data.buffer_list.buffers[i].fname); - dict_unref(entry->data.buffer_list.buffers[i].additional_data); + tv_dict_unref(entry->data.buffer_list.buffers[i].additional_data); } xfree(entry->data.buffer_list.buffers); break; @@ -3451,7 +3451,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, "cannot be converted to a VimL dictionary")), \ initial_fpos); \ ga_clear(&ad_ga); \ - clear_tv(&adtv); \ + tv_clear(&adtv); \ goto shada_read_next_item_error; \ } \ tgt = adtv.vval.v_dict; \ @@ -3474,7 +3474,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, if (msgpack_to_vim(obj, &aetv) == FAIL) { \ emsgf(_(READERR(name, "cannot be converted to a VimL list")), \ initial_fpos); \ - clear_tv(&aetv); \ + tv_clear(&aetv); \ goto shada_read_next_item_error; \ } \ assert(aetv.v_type == VAR_LIST); \ @@ -3866,7 +3866,7 @@ shada_read_next_item_hist_no_conv: &tgttv, true, 0); - clear_tv(&entry->data.global_var.value); + tv_clear(&entry->data.global_var.value); entry->data.global_var.value = tgttv; } SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2, diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 12f982106a..d4f49bffb2 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2309,10 +2309,11 @@ int captype(char_u *word, char_u *end) for (p = word; !spell_iswordp_nmw(p, curwin); mb_ptr_adv(p)) if (end == NULL ? *p == NUL : p >= end) return 0; // only non-word characters, illegal word - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else + if (has_mbyte) { + c = mb_ptr2char_adv((const char_u **)&p); + } else { c = *p++; + } firstcap = allcap = SPELL_ISUPPER(c); // Need to check all letters to find a word with mixed upper/lower. @@ -2607,14 +2608,15 @@ static bool spell_iswordp(char_u *p, win_T *wp) // Returns true if "p" points to a word character. // Unlike spell_iswordp() this doesn't check for "midword" characters. -bool spell_iswordp_nmw(char_u *p, win_T *wp) +bool spell_iswordp_nmw(const char_u *p, win_T *wp) { int c; if (has_mbyte) { c = mb_ptr2char(p); - if (c > 255) + if (c > 255) { return spell_mb_isword_class(mb_get_class(p), wp); + } return spelltab.st_isw[c]; } return spelltab.st_isw[*p]; @@ -2623,7 +2625,7 @@ bool spell_iswordp_nmw(char_u *p, win_T *wp) // Returns true if word class indicates a word character. // Only for characters above 255. // Unicode subscript and superscript are not considered word characters. -// See also dbcs_class() and utf_class() in mbyte.c. +// See also utf_class() in mbyte.c. static bool spell_mb_isword_class(int cl, win_T *wp) { if (wp->w_s->b_cjk) @@ -2646,12 +2648,7 @@ static bool spell_iswordp_w(int *p, win_T *wp) s = p; if (*s > 255) { - if (enc_utf8) - return spell_mb_isword_class(utf_class(*s), wp); - if (enc_dbcs) - return spell_mb_isword_class( - dbcs_class((unsigned)*s >> 8, *s & 0xff), wp); - return false; + return spell_mb_isword_class(utf_class(*s), wp); } return spelltab.st_isw[*s]; } @@ -2680,7 +2677,7 @@ int spell_casefold(char_u *str, int len, char_u *buf, int buflen) buf[outi] = NUL; return FAIL; } - c = mb_cptr2char_adv(&p); + c = mb_cptr2char_adv((const char_u **)&p); outi += mb_char2bytes(SPELL_TOFOLD(c), buf + outi); } buf[outi] = NUL; @@ -2942,7 +2939,7 @@ void spell_suggest(int count) // For redo we use a change-word command. ResetRedobuff(); - AppendToRedobuff((char_u *)"ciw"); + AppendToRedobuff("ciw"); AppendToRedobuffLit(p + c, stp->st_wordlen + sug.su_badlen - stp->st_orglen); AppendCharToRedobuff(ESC); @@ -3237,7 +3234,7 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) list_T *list; listitem_T *li; int score; - char_u *p; + const char *p; // The work is split up in a few parts to avoid having to export // suginfo_T. @@ -3249,11 +3246,12 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) if (li->li_tv.v_type == VAR_LIST) { // Get the word and the score from the items. score = get_spellword(li->li_tv.vval.v_list, &p); - if (score >= 0 && score <= su->su_maxscore) - add_suggestion(su, &su->su_ga, p, su->su_badlen, - score, 0, true, su->su_sallang, false); + if (score >= 0 && score <= su->su_maxscore) { + add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen, + score, 0, true, su->su_sallang, false); + } } - list_unref(list); + tv_list_unref(list); } // Remove bogus suggestions, sort and truncate at "maxcount". @@ -3410,17 +3408,19 @@ void onecap_copy(char_u *word, char_u *wcopy, bool upper) int l; p = word; - if (has_mbyte) - c = mb_cptr2char_adv(&p); - else + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&p); + } else { c = *p++; - if (upper) + } + if (upper) { c = SPELL_TOUPPER(c); - else + } else { c = SPELL_TOFOLD(c); - if (has_mbyte) + } + if (has_mbyte) { l = mb_char2bytes(c, wcopy); - else { + } else { l = 1; wcopy[0] = c; } @@ -3437,10 +3437,11 @@ static void allcap_copy(char_u *word, char_u *wcopy) d = wcopy; for (s = word; *s != NUL; ) { - if (has_mbyte) - c = mb_cptr2char_adv(&s); - else + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&s); + } else { c = *s++; + } if (c == 0xdf) { c = 'S'; @@ -5621,7 +5622,7 @@ static void add_suggestion ( suginfo_T *su, garray_T *gap, // either su_ga or su_sga - char_u *goodword, + const char_u *goodword, int badlenarg, // len of bad word replaced with "goodword" int score, int altscore, @@ -5635,13 +5636,11 @@ add_suggestion ( int badlen; // len of bad word changed suggest_T *stp; suggest_T new_sug; - int i; - char_u *pgood, *pbad; // Minimize "badlen" for consistency. Avoids that changing "the the" to // "thee the" is added next to changing the first "the" the "thee". - pgood = goodword + STRLEN(goodword); - pbad = su->su_badptr + badlenarg; + const char_u *pgood = goodword + STRLEN(goodword); + char_u *pbad = su->su_badptr + badlenarg; for (;; ) { goodlen = (int)(pgood - goodword); badlen = (int)(pbad - su->su_badptr); @@ -5661,9 +5660,10 @@ add_suggestion ( // the first "the" to itself. return; - if (GA_EMPTY(gap)) + int i; + if (GA_EMPTY(gap)) { i = -1; - else { + } else { // Check if the word is already there. Also check the length that is // being replaced "thes," -> "these" is a different suggestion from // "thes" -> "these". @@ -5862,27 +5862,31 @@ cleanup_suggestions ( return maxscore; } -// Soundfold a string, for soundfold(). -// Result is in allocated memory, NULL for an error. -char_u *eval_soundfold(char_u *word) +/// Soundfold a string, for soundfold() +/// +/// @param[in] word Word to soundfold. +/// +/// @return [allocated] soundfolded string or NULL in case of error. May return +/// copy of the input string if soundfolding is not +/// supported by any of the languages in &spellang. +char *eval_soundfold(const char *const word) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - langp_T *lp; - char_u sound[MAXWLEN]; - if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { // Use the sound-folding of the first language that supports it. - for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) { - lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); + for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) { + langp_T *const lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); if (!GA_EMPTY(&lp->lp_slang->sl_sal)) { // soundfold the word - spell_soundfold(lp->lp_slang, word, false, sound); - return vim_strsave(sound); + char_u sound[MAXWLEN]; + spell_soundfold(lp->lp_slang, (char_u *)word, false, sound); + return xstrdup((const char *)sound); } } } // No language with sound folding, return word as-is. - return vim_strsave(word); + return xstrdup(word); } /// Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]". @@ -5939,12 +5943,12 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res) // The sl_sal_first[] table contains the translation for chars up to // 255, sl_sal the rest. for (s = inword; *s != NUL; ) { - c = mb_cptr2char_adv(&s); - if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) + c = mb_cptr2char_adv((const char_u **)&s); + if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) { c = ' '; - else if (c < 256) + } else if (c < 256) { c = slang->sl_sal_first[c]; - else { + } else { ip = ((int **)slang->sl_sal.ga_data)[c & 0xff]; if (ip == NULL) // empty list, can't match c = NUL; @@ -6229,9 +6233,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) int word[MAXWLEN]; int wres[MAXWLEN]; int l; - char_u *s; int *ws; - char_u *t; int *pf; int i, j, z; int reslen; @@ -6251,9 +6253,9 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) // Remove accents, if wanted. We actually remove all non-word characters. // But keep white space. wordlen = 0; - for (s = inword; *s != NUL; ) { - t = s; - c = mb_cptr2char_adv(&s); + for (const char_u *s = inword; *s != NUL; ) { + const char_u *t = s; + c = mb_cptr2char_adv((const char_u **)&s); if (slang->sl_rem_accents) { if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) { if (did_white) @@ -6262,8 +6264,9 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) did_white = true; } else { did_white = false; - if (!spell_iswordp_nmw(t, curwin)) + if (!spell_iswordp_nmw(t, curwin)) { continue; + } } } word[wordlen++] = c; @@ -6310,7 +6313,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) continue; ++k; } - s = smp[n].sm_rules; + char_u *s = smp[n].sm_rules; pri = 5; // default priority p0 = *s; @@ -6709,25 +6712,30 @@ soundalike_score ( // support multi-byte characters. static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword) { - int *cnt; - int badlen, goodlen; // lengths including NUL + int *cnt; int j, i; int t; int bc, gc; int pbc, pgc; - char_u *p; int wbadword[MAXWLEN]; int wgoodword[MAXWLEN]; const bool l_has_mbyte = has_mbyte; + // Lengths with NUL. + int badlen; + int goodlen; if (l_has_mbyte) { // Get the characters from the multi-byte strings and put them in an // int array for easy access. - for (p = badword, badlen = 0; *p != NUL; ) + badlen = 0; + for (const char_u *p = badword; *p != NUL; ) { wbadword[badlen++] = mb_cptr2char_adv(&p); + } wbadword[badlen++] = 0; - for (p = goodword, goodlen = 0; *p != NUL; ) + goodlen = 0; + for (const char_u *p = goodword; *p != NUL; ) { wgoodword[goodlen++] = mb_cptr2char_adv(&p); + } wgoodword[goodlen++] = 0; } else { badlen = (int)STRLEN(badword) + 1; @@ -6961,19 +6969,20 @@ static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goo int score_off; int minscore; int round; - char_u *p; int wbadword[MAXWLEN]; int wgoodword[MAXWLEN]; // Get the characters from the multi-byte strings and put them in an // int array for easy access. bi = 0; - for (p = badword; *p != NUL; ) + for (const char_u *p = badword; *p != NUL; ) { wbadword[bi++] = mb_cptr2char_adv(&p); + } wbadword[bi++] = 0; gi = 0; - for (p = goodword; *p != NUL; ) + for (const char_u *p = goodword; *p != NUL; ) { wgoodword[gi++] = mb_cptr2char_adv(&p); + } wgoodword[gi++] = 0; // The idea is to go from start to end over the words. So long as @@ -7139,16 +7148,17 @@ void ex_spelldump(exarg_T *eap) char_u *spl; long dummy; - if (no_spell_checking(curwin)) + if (no_spell_checking(curwin)) { return; - get_option_value((char_u*)"spl", &dummy, &spl, OPT_LOCAL); + } + get_option_value((char_u *)"spl", &dummy, &spl, OPT_LOCAL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); // enable spelling locally in the new window - set_option_value((char_u*)"spell", true, (char_u*)"", OPT_LOCAL); - set_option_value((char_u*)"spl", dummy, spl, OPT_LOCAL); + set_option_value("spell", true, "", OPT_LOCAL); + set_option_value("spl", dummy, (char *)spl, OPT_LOCAL); xfree(spl); if (!bufempty()) { diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index c108ae4a2c..4d7ff558ad 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -1435,7 +1435,7 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // First count the number of items for each list. Temporarily use // sl_sal_first[] for this. for (p = from, s = to; *p != NUL && *s != NUL; ) { - c = mb_cptr2char_adv(&p); + c = mb_cptr2char_adv((const char_u **)&p); mb_cptr_adv(s); if (c >= 256) ++lp->sl_sal_first[c & 0xff]; @@ -1455,8 +1455,8 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // list. memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256); for (p = from, s = to; *p != NUL && *s != NUL; ) { - c = mb_cptr2char_adv(&p); - i = mb_cptr2char_adv(&s); + c = mb_cptr2char_adv((const char_u **)&p); + i = mb_cptr2char_adv((const char_u **)&s); if (c >= 256) { // Append the from-to chars at the end of the list with // the low byte. @@ -1542,8 +1542,9 @@ static int *mb_str2wide(char_u *s) int i = 0; int *res = xmalloc((mb_charlen(s) + 1) * sizeof(int)); - for (char_u *p = s; *p != NUL; ) - res[i++] = mb_ptr2char_adv(&p); + for (char_u *p = s; *p != NUL; ) { + res[i++] = mb_ptr2char_adv((const char_u **)&p); + } res[i] = NUL; return res; @@ -2486,13 +2487,14 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) // Check that every character appears only once. for (p = items[1]; *p != NUL; ) { - c = mb_ptr2char_adv(&p); + c = mb_ptr2char_adv((const char_u **)&p); if ((!GA_EMPTY(&spin->si_map) && vim_strchr(spin->si_map.ga_data, c) != NULL) - || vim_strchr(p, c) != NULL) + || vim_strchr(p, c) != NULL) { smsg(_("Duplicate character in MAP in %s line %d"), fname, lnum); + } } // We simply concatenate all the MAP strings, separated by @@ -2717,12 +2719,12 @@ static unsigned get_affitem(int flagtype, char_u **pp) } res = getdigits_int(pp); } else { - res = mb_ptr2char_adv(pp); + res = mb_ptr2char_adv((const char_u **)pp); if (flagtype == AFT_LONG || (flagtype == AFT_CAPLONG && res >= 'A' && res <= 'Z')) { if (**pp == NUL) return 0; - res = mb_ptr2char_adv(pp) + (res << 16); + res = mb_ptr2char_adv((const char_u **)pp) + (res << 16); } } return res; @@ -2823,12 +2825,14 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag) case AFT_CAPLONG: case AFT_LONG: for (p = afflist; *p != NUL; ) { - n = mb_ptr2char_adv(&p); + n = mb_ptr2char_adv((const char_u **)&p); if ((flagtype == AFT_LONG || (n >= 'A' && n <= 'Z')) - && *p != NUL) - n = mb_ptr2char_adv(&p) + (n << 16); - if (n == flag) + && *p != NUL) { + n = mb_ptr2char_adv((const char_u **)&p) + (n << 16); + } + if (n == flag) { return true; + } } break; @@ -5436,10 +5440,11 @@ static void init_spellfile(void) fname = LANGP_ENTRY(curwin->w_s->b_langp, 0) ->lp_slang->sl_fname; vim_snprintf((char *)buf + l, MAXPATHL - l, ".%s.add", - fname != NULL - && strstr((char *)path_tail(fname), ".ascii.") != NULL - ? (char_u *)"ascii" : spell_enc()); - set_option_value((char_u *)"spellfile", 0L, buf, OPT_LOCAL); + ((fname != NULL + && strstr((char *)path_tail(fname), ".ascii.") != NULL) + ? "ascii" + : (const char *)spell_enc())); + set_option_value("spellfile", 0L, (const char *)buf, OPT_LOCAL); break; } aspath = false; @@ -5465,9 +5470,9 @@ static int set_spell_chartab(char_u *fol, char_u *low, char_u *upp) EMSG(_(e_affform)); return FAIL; } - f = mb_ptr2char_adv(&pf); - l = mb_ptr2char_adv(&pl); - u = mb_ptr2char_adv(&pu); + f = mb_ptr2char_adv((const char_u **)&pf); + l = mb_ptr2char_adv((const char_u **)&pl); + u = mb_ptr2char_adv((const char_u **)&pu); // Every character that appears is a word character. if (f < 256) new_st.st_isw[f] = true; @@ -5532,7 +5537,7 @@ set_spell_charflags ( } if (*p != NUL) { - c = mb_ptr2char_adv(&p); + c = mb_ptr2char_adv((const char_u **)&p); new_st.st_fold[i + 128] = c; if (i + 128 != c && new_st.st_isu[i + 128] && c < 256) new_st.st_upper[c] = i + 128; @@ -5619,12 +5624,13 @@ static void set_map_str(slang_T *lp, char_u *map) // "aaa/bbb/ccc/". Fill sl_map_array[c] with the character before c and // before the same slash. For characters above 255 sl_map_hash is used. for (p = map; *p != NUL; ) { - c = mb_cptr2char_adv(&p); - if (c == '/') + c = mb_cptr2char_adv((const char_u **)&p); + if (c == '/') { headc = 0; - else { - if (headc == 0) + } else { + if (headc == 0) { headc = c; + } // Characters above 255 don't fit in sl_map_array[], put them in // the hash table. Each entry is the char, a NUL the headchar and diff --git a/src/nvim/strings.c b/src/nvim/strings.c index d03970108b..5dcffe00e0 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -291,30 +291,33 @@ void vim_strup(char_u *p) } } -/* - * Make string "s" all upper-case and return it in allocated memory. - * Handles multi-byte characters as well as possible. - */ -char_u *strup_save(const char_u *orig) +/// Make given string all upper-case +/// +/// Handels multi-byte characters as good as possible. +/// +/// @param[in] orig Input string. +/// +/// @return [allocated] upper-cased string. +char *strup_save(const char *const orig) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char_u *res = vim_strsave(orig); + char *res = xstrdup(orig); - char_u *p = res; + char *p = res; while (*p != NUL) { int l; if (enc_utf8) { - int c = utf_ptr2char(p); + int c = utf_ptr2char((const char_u *)p); int uc = utf_toupper(c); - /* Reallocate string when byte count changes. This is rare, - * thus it's OK to do another malloc()/free(). */ - l = utf_ptr2len(p); + // Reallocate string when byte count changes. This is rare, + // thus it's OK to do another malloc()/free(). + l = utf_ptr2len((const char_u *)p); int newl = utf_char2len(uc); if (newl != l) { // TODO(philix): use xrealloc() in strup_save() - char_u *s = xmalloc(STRLEN(res) + (size_t)(1 + newl - l)); + char *s = xmalloc(STRLEN(res) + (size_t)(1 + newl - l)); memcpy(s, res, (size_t)(p - res)); STRCPY(s + (p - res) + newl, p + l); p = s + (p - res); @@ -322,12 +325,13 @@ char_u *strup_save(const char_u *orig) res = s; } - utf_char2bytes(uc, p); + utf_char2bytes(uc, (char_u *)p); p += newl; - } else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) - p += l; /* skip multi-byte character */ - else { - *p = (char_u) TOUPPER_LOC(*p); // note that toupper() can be a macro + } else if (has_mbyte && (l = (*mb_ptr2len)((const char_u *)p)) > 1) { + p += l; // Skip multi-byte character. + } else { + // note that toupper() can be a macro + *p = (char)(uint8_t)TOUPPER_LOC(*p); p++; } } @@ -571,8 +575,8 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) EMSG(_(e_printf)); } else { (*idxp)++; - int err = false; - n = (varnumber_T)get_tv_number_chk(&tvs[idx], &err); + bool err = false; + n = tv_get_number_chk(&tvs[idx], &err); if (err) { n = 0; } @@ -594,22 +598,21 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) /// free "*tofree". /// /// @return String value or NULL in case of error. -static char *tv_str(typval_T *tvs, int *idxp, char ** const tofree) +static const char *tv_str(typval_T *tvs, int *idxp, char **const tofree) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int idx = *idxp - 1; - char *s = NULL; + const char *s = NULL; if (tvs[idx].v_type == VAR_UNKNOWN) { EMSG(_(e_printf)); } else { (*idxp)++; if (tvs[idx].v_type == VAR_STRING || tvs[idx].v_type == VAR_NUMBER) { - s = (char *)get_tv_string_chk(&tvs[idx]); + s = tv_get_string_chk(&tvs[idx]); *tofree = NULL; } else { - s = encode_tv2echo(&tvs[idx], NULL); - *tofree = s; + s = *tofree = encode_tv2echo(&tvs[idx], NULL); } } return s; @@ -753,6 +756,22 @@ int vim_snprintf(char *str, size_t str_m, const char *fmt, ...) return str_l; } +// Return the representation of infinity for printf() function: +// "-inf", "inf", "+inf", " inf", "-INF", "INF", "+INF" or " INF". +static const char *infinity_str(bool positive, char fmt_spec, + int force_sign, int space_for_positive) +{ + static const char *table[] = { + "-inf", "inf", "+inf", " inf", + "-INF", "INF", "+INF", " INF" + }; + int idx = positive * (1 + force_sign + force_sign * space_for_positive); + if (ASCII_ISUPPER(fmt_spec)) { + idx += 4; + } + return table[idx]; +} + /// Write formatted value to the string /// @@ -909,7 +928,6 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; - case 'F': fmt_spec = 'f'; break; default: break; } @@ -934,7 +952,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, case 's': case 'S': str_arg = tvs ? tv_str(tvs, &arg_idx, &tofree) - : va_arg(ap, char *); + : va_arg(ap, const char *); if (!str_arg) { str_arg = "[NULL]"; str_arg_l = 6; @@ -1186,6 +1204,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } case 'f': + case 'F': case 'e': case 'E': case 'g': @@ -1201,36 +1220,51 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, if (fmt_spec == 'g' || fmt_spec == 'G') { // can't use %g directly, cause it prints "1.0" as "1" if ((abs_f >= 0.001 && abs_f < 10000000.0) || abs_f == 0.0) { - fmt_spec = 'f'; + fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f'; } else { fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; } remove_trailing_zeroes = true; } - if (fmt_spec == 'f' && abs_f > 1.0e307) { - // avoid a buffer overflow - memmove(tmp, "inf", sizeof("inf")); - str_arg_l = sizeof("inf") - 1; + if (isinf(f) + || (strchr("fF", fmt_spec) != NULL && abs_f > 1.0e307)) { + xstrlcpy(tmp, infinity_str(f > 0.0, fmt_spec, + force_sign, space_for_positive), + sizeof(tmp)); + str_arg_l = strlen(tmp); + zero_padding = 0; + } else if (isnan(f)) { + // Not a number: nan or NAN + memmove(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan", 4); + str_arg_l = 3; + zero_padding = 0; } else { format[0] = '%'; - int l = 1; + size_t l = 1; + if (force_sign) { + format[l++] = space_for_positive ? ' ' : '+'; + } if (precision_specified) { size_t max_prec = TMP_LEN - 10; // make sure we don't get more digits than we have room for - if (fmt_spec == 'f' && abs_f > 1.0) { + if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) { max_prec -= (size_t)log10(abs_f); } if (precision > max_prec) { precision = max_prec; } - l += snprintf(format + 1, sizeof(format) - 1, ".%d", - (int)precision); + l += (size_t)snprintf(format + l, sizeof(format) - l, ".%d", + (int)precision); } + + // Cast to char to avoid a conversion warning on Ubuntu 12.04. format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); format[l + 1] = NUL; - assert(l + 1 < (int)sizeof(format)); + + // Regular float number + assert(l + 1 < sizeof(format)); str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); assert(str_arg_l < sizeof(tmp)); @@ -1239,7 +1273,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, char *tp; // using %g or %G: remove superfluous zeroes - if (fmt_spec == 'f') { + if (fmt_spec == 'f' || fmt_spec == 'F') { tp = tmp + str_arg_l - 1; } else { tp = (char *)vim_strchr((char_u *)tmp, @@ -1281,6 +1315,12 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } } } + if (zero_padding && min_field_width > str_arg_l + && (tmp[0] == '-' || force_sign)) { + // Padding 0's should be inserted after the sign. + number_of_zeros_to_pad = min_field_width - str_arg_l; + zero_padding_insertion_ind = 1; + } str_arg = tmp; break; } diff --git a/src/nvim/strings.h b/src/nvim/strings.h index eb8b83c7d0..59b8701a3f 100644 --- a/src/nvim/strings.h +++ b/src/nvim/strings.h @@ -1,12 +1,11 @@ #ifndef NVIM_STRINGS_H #define NVIM_STRINGS_H -#include <stdarg.h> #include <stdbool.h> -#include <stddef.h> +#include <stdarg.h> #include "nvim/types.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "strings.h.generated.h" diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 0a27d9dd92..e36b00d770 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -42,34 +42,38 @@ static bool did_syntax_onoff = false; -// Structure that stores information about a highlight group. -// The ID of a highlight group is also called group ID. It is the index in -// the highlight_ga array PLUS ONE. +/// Structure that stores information about a highlight group. +/// The ID of a highlight group is also called group ID. It is the index in +/// the highlight_ga array PLUS ONE. struct hl_group { - char_u *sg_name; // highlight group name - char_u *sg_name_u; // uppercase of sg_name - int sg_attr; // Screen attr - int sg_link; // link to this highlight group ID - int sg_set; // combination of SG_* flags - scid_T sg_scriptID; // script in which the group was last set + char_u *sg_name; ///< highlight group name + char_u *sg_name_u; ///< uppercase of sg_name + int sg_attr; ///< Screen attr @see ATTR_ENTRY + int sg_link; ///< link to this highlight group ID + int sg_set; ///< combination of flags in \ref SG_SET + scid_T sg_scriptID; ///< script in which the group was last set // for terminal UIs - int sg_cterm; // "cterm=" highlighting attr - int sg_cterm_fg; // terminal fg color number + 1 - int sg_cterm_bg; // terminal bg color number + 1 - int sg_cterm_bold; // bold attr was set for light color + int sg_cterm; ///< "cterm=" highlighting attr + int sg_cterm_fg; ///< terminal fg color number + 1 + int sg_cterm_bg; ///< terminal bg color number + 1 + int sg_cterm_bold; ///< bold attr was set for light color // for RGB UIs - int sg_gui; // "gui=" highlighting attributes - RgbValue sg_rgb_fg; // RGB foreground color - RgbValue sg_rgb_bg; // RGB background color - RgbValue sg_rgb_sp; // RGB special color - uint8_t *sg_rgb_fg_name; // RGB foreground color name - uint8_t *sg_rgb_bg_name; // RGB background color name - uint8_t *sg_rgb_sp_name; // RGB special color name + int sg_gui; ///< "gui=" highlighting attributes + ///< (combination of \ref HL_ATTRIBUTES) + RgbValue sg_rgb_fg; ///< RGB foreground color + RgbValue sg_rgb_bg; ///< RGB background color + RgbValue sg_rgb_sp; ///< RGB special color + uint8_t *sg_rgb_fg_name; ///< RGB foreground color name + uint8_t *sg_rgb_bg_name; ///< RGB background color name + uint8_t *sg_rgb_sp_name; ///< RGB special color name }; +/// \addtogroup SG_SET +/// @{ #define SG_CTERM 2 // cterm has been set #define SG_GUI 4 // gui has been set #define SG_LINK 8 // link has been set +/// @} // highlight groups for 'highlight' option static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; @@ -1406,14 +1410,14 @@ static int syn_stack_equal(synstate_T *sp) /* If the pointer is different it can still be the * same text. Compare the strings, ignore case when * the start item has the sp_ic flag set. */ - if (bsx->matches[j] == NULL - || six->matches[j] == NULL) + if (bsx->matches[j] == NULL || six->matches[j] == NULL) { break; - if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic - ? mb_stricmp(bsx->matches[j], - six->matches[j]) != 0 - : STRCMP(bsx->matches[j], six->matches[j]) != 0) + } + if (mb_strcmp_ic((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic, + (const char *)bsx->matches[j], + (const char *)six->matches[j]) != 0) { break; + } } } if (j != NSUBEXP) @@ -3259,9 +3263,10 @@ static void syn_cmd_clear(exarg_T *eap, int syncing) syntax_sync_clear(); else { syntax_clear(curwin->w_s); - if (curwin->w_s == &curwin->w_buffer->b_s) - do_unlet((char_u *)"b:current_syntax", TRUE); - do_unlet((char_u *)"w:current_syntax", TRUE); + if (curwin->w_s == &curwin->w_buffer->b_s) { + do_unlet(S_LEN("b:current_syntax"), true); + } + do_unlet(S_LEN("w:current_syntax"), true); } } else { /* @@ -3337,7 +3342,7 @@ static void syn_cmd_enable(exarg_T *eap, int syncing) { set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable"); syn_cmd_onoff(eap, "syntax"); - do_unlet((char_u *)"g:syntax_cmd", TRUE); + do_unlet(S_LEN("g:syntax_cmd"), true); } /* @@ -3350,7 +3355,7 @@ static void syn_cmd_reset(exarg_T *eap, int syncing) if (!eap->skip) { set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset"); do_cmdline_cmd("runtime! syntax/syncolor.vim"); - do_unlet((char_u *)"g:syntax_cmd", TRUE); + do_unlet(S_LEN("g:syntax_cmd"), true); } } @@ -5537,10 +5542,10 @@ void ex_ownsyntax(exarg_T *eap) set_internal_string_var((char_u *)"w:current_syntax", new_value); } - /* restore value of b:current_syntax */ - if (old_value == NULL) - do_unlet((char_u *)"b:current_syntax", TRUE); - else { + // Restore value of b:current_syntax. + if (old_value == NULL) { + do_unlet(S_LEN("b:current_syntax"), true); + } else { set_internal_string_var((char_u *)"b:current_syntax", old_value); xfree(old_value); } @@ -5573,43 +5578,42 @@ void reset_expand_highlight(void) * Handle command line completion for :match and :echohl command: Add "None" * as highlight group. */ -void set_context_in_echohl_cmd(expand_T *xp, char_u *arg) +void set_context_in_echohl_cmd(expand_T *xp, const char *arg) { xp->xp_context = EXPAND_HIGHLIGHT; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_none = 1; } /* * Handle command line completion for :syntax command. */ -void set_context_in_syntax_cmd(expand_T *xp, char_u *arg) +void set_context_in_syntax_cmd(expand_T *xp, const char *arg) { - char_u *p; - - /* Default: expand subcommands */ + // Default: expand subcommands. xp->xp_context = EXPAND_SYNTAX; expand_what = EXP_SUBCMD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_link = 0; include_default = 0; /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past first word */ - xp->xp_pattern = skipwhite(p); - if (*skiptowhite(xp->xp_pattern) != NUL) + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past first word. + xp->xp_pattern = skipwhite((const char_u *)p); + if (*skiptowhite(xp->xp_pattern) != NUL) { xp->xp_context = EXPAND_NOTHING; - else if (STRNICMP(arg, "case", p - arg) == 0) + } else if (STRNICMP(arg, "case", p - arg) == 0) { expand_what = EXP_CASE; - else if ( STRNICMP(arg, "keyword", p - arg) == 0 + } else if (STRNICMP(arg, "keyword", p - arg) == 0 || STRNICMP(arg, "region", p - arg) == 0 || STRNICMP(arg, "match", p - arg) == 0 - || STRNICMP(arg, "list", p - arg) == 0) + || STRNICMP(arg, "list", p - arg) == 0) { xp->xp_context = EXPAND_HIGHLIGHT; - else + } else { xp->xp_context = EXPAND_NOTHING; + } } } } @@ -5912,6 +5916,7 @@ static char *highlight_init_both[] = "default link EndOfBuffer NonText", "default link QuickFixLine Search", "default link Substitute Search", + "default link Whitespace NonText", NULL }; @@ -6093,16 +6098,16 @@ int load_colors(char_u *name) return retval; } -/* - * Handle the ":highlight .." command. - * When using ":hi clear" this is called recursively for each group with - * "forceit" and "init" both TRUE. - */ -void -do_highlight ( + +/// Handle the ":highlight .." command. +/// When using ":hi clear" this is called recursively for each group with +/// "forceit" and "init" both TRUE. +/// @param init TRUE when called for initializing +void +do_highlight( char_u *line, int forceit, - int init /* TRUE when called for initializing */ + int init ) { char_u *name_end; @@ -6231,7 +6236,7 @@ do_highlight ( */ line = linep; if (ends_excmd(*line)) { - do_unlet((char_u *)"colors_name", TRUE); + do_unlet(S_LEN("colors_name"), true); restore_cterm_colors(); /* @@ -6510,16 +6515,16 @@ do_highlight ( if (!ui_rgb_attached()) { must_redraw = CLEAR; if (color >= 0) { - if (t_colors < 16) + if (t_colors < 16) { i = (color == 0 || color == 4); - else + } else { i = (color < 7 || color == 8); - /* Set the 'background' option if the value is - * wrong. */ - if (i != (*p_bg == 'd')) - set_option_value((char_u *)"bg", 0L, - i ? (char_u *)"dark" - : (char_u *)"light", 0); + } + // Set the 'background' option if the value is + // wrong. + if (i != (*p_bg == 'd')) { + set_option_value("bg", 0L, (i ? "dark" : "light"), 0); + } } } } @@ -6704,12 +6709,10 @@ static garray_T attr_table = GA_EMPTY_INIT_VALUE; #define ATTR_ENTRY(idx) ((attrentry_T *)attr_table.ga_data)[idx] -/* - * Return the attr number for a set of colors and font. - * Add a new entry to the term_attr_table, attr_table or gui_attr_table - * if the combination is new. - * Return 0 for error. - */ +/// Return the attr number for a set of colors and font. +/// Add a new entry to the term_attr_table, attr_table or gui_attr_table +/// if the combination is new. +/// @return 0 for error. int get_attr_entry(attrentry_T *aep) { garray_T *table = &attr_table; @@ -6930,21 +6933,21 @@ static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg return didh; } -/* - * Return "1" if highlight group "id" has attribute "flag". - * Return NULL otherwise. - */ -char_u * -highlight_has_attr ( - int id, - int flag, - int modec // 'g' for GUI, 'c' for cterm -) +/// Check whether highlight group has attribute +/// +/// @param[in] id Highlight group to check. +/// @param[in] flag Attribute to check. +/// @param[in] modec 'g' for GUI, 'c' for term. +/// +/// @return "1" if highlight group has attribute, NULL otherwise. +const char *highlight_has_attr(const int id, const int flag, const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { int attr; - if (id <= 0 || id > highlight_ga.ga_len) + if (id <= 0 || id > highlight_ga.ga_len) { return NULL; + } if (modec == 'g') { attr = HL_TABLE()[id - 1].sg_gui; @@ -6952,39 +6955,42 @@ highlight_has_attr ( attr = HL_TABLE()[id - 1].sg_cterm; } - if (attr & flag) - return (char_u *)"1"; - return NULL; + return (attr & flag) ? "1" : NULL; } -/* - * Return color name of highlight group "id". - */ -char_u * -highlight_color ( - int id, - char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */ - int modec /* 'g' for GUI, 'c' for cterm, 't' for term */ -) +/// Return color name of the given highlight group +/// +/// @param[in] id Highlight group to work with. +/// @param[in] what What to return: one of "font", "fg", "bg", "sp", "fg#", +/// "bg#" or "sp#". +/// @param[in] modec 'g' for GUI, 'c' for cterm and 't' for term. +/// +/// @return color name, possibly in a static buffer. Buffer will be overwritten +/// on next highlight_color() call. May return NULL. +const char *highlight_color(const int id, const char *const what, + const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - static char_u name[20]; + static char name[20]; int n; - int fg = FALSE; - int sp = FALSE; - int font = FALSE; + bool fg = false; + bool sp = false; + bool font = false; - if (id <= 0 || id > highlight_ga.ga_len) + if (id <= 0 || id > highlight_ga.ga_len) { return NULL; + } - if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') - fg = TRUE; - else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' - && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') - font = TRUE; - else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') - sp = TRUE; - else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) + if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') { + fg = true; + } else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' + && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') { + font = true; + } else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') { + sp = true; + } else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) { return NULL; + } if (modec == 'g') { if (what[2] == '#' && ui_rgb_attached()) { if (fg) { @@ -6997,19 +7003,20 @@ highlight_color ( if (n < 0 || n > 0xffffff) { return NULL; } - snprintf((char *)name, sizeof(name), "#%06x", n); + snprintf(name, sizeof(name), "#%06x", n); return name; } if (fg) { - return HL_TABLE()[id - 1].sg_rgb_fg_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_fg_name; } if (sp) { - return HL_TABLE()[id - 1].sg_rgb_sp_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_sp_name; } - return HL_TABLE()[id - 1].sg_rgb_bg_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_bg_name; } - if (font || sp) + if (font || sp) { return NULL; + } if (modec == 'c') { if (fg) { n = HL_TABLE()[id - 1].sg_cterm_fg - 1; @@ -7019,10 +7026,10 @@ highlight_color ( if (n < 0) { return NULL; } - snprintf((char *)name, sizeof(name), "%d", n); + snprintf(name, sizeof(name), "%d", n); return name; } - /* term doesn't have color */ + // term doesn't have color. return NULL; } @@ -7113,7 +7120,7 @@ set_hl_attr ( * Lookup a highlight group name and return it's ID. * If it is not found, 0 is returned. */ -int syn_name2id(char_u *name) +int syn_name2id(const char_u *name) { int i; char_u name_u[200]; @@ -7133,7 +7140,7 @@ int syn_name2id(char_u *name) /* * Return TRUE if highlight group "name" exists. */ -int highlight_exists(char_u *name) +int highlight_exists(const char_u *name) { return syn_name2id(name) > 0; } @@ -7161,12 +7168,13 @@ int syn_namen2id(char_u *linep, int len) return id; } -/* - * Find highlight group name in the table and return it's ID. - * The argument is a pointer to the name and the length of the name. - * If it doesn't exist yet, a new entry is created. - * Return 0 for failure. - */ +/// Find highlight group name in the table and return it's ID. +/// If it doesn't exist yet, a new entry is created. +/// +/// @param pp Highlight group name +/// @param len length of \p pp +/// +/// @return 0 for failure else the id of the group int syn_check_group(char_u *pp, int len) { char_u *name = vim_strnsave(pp, len); @@ -7469,41 +7477,41 @@ int highlight_changed(void) /* * Handle command line completion for :highlight command. */ -void set_context_in_highlight_cmd(expand_T *xp, char_u *arg) +void set_context_in_highlight_cmd(expand_T *xp, const char *arg) { - char_u *p; - - /* Default: expand group names */ + // Default: expand group names. xp->xp_context = EXPAND_HIGHLIGHT; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_link = 2; include_default = 1; /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past "default" or group name */ + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past "default" or group name. include_default = 0; - if (STRNCMP("default", arg, p - arg) == 0) { - arg = skipwhite(p); - xp->xp_pattern = arg; - p = skiptowhite(arg); + if (strncmp("default", arg, p - arg) == 0) { + arg = (const char *)skipwhite((const char_u *)p); + xp->xp_pattern = (char_u *)arg; + p = (const char *)skiptowhite((const char_u *)arg); } if (*p != NUL) { /* past group name */ include_link = 0; - if (arg[1] == 'i' && arg[0] == 'N') + if (arg[1] == 'i' && arg[0] == 'N') { highlight_list(); - if (STRNCMP("link", arg, p - arg) == 0 - || STRNCMP("clear", arg, p - arg) == 0) { - xp->xp_pattern = skipwhite(p); - p = skiptowhite(xp->xp_pattern); - if (*p != NUL) { /* past first group name */ - xp->xp_pattern = skipwhite(p); - p = skiptowhite(xp->xp_pattern); + } + if (strncmp("link", arg, p - arg) == 0 + || strncmp("clear", arg, p - arg) == 0) { + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); + if (*p != NUL) { // Past first group name. + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); } } - if (*p != NUL) /* past group name(s) */ + if (*p != NUL) { // Past group name(s). xp->xp_context = EXPAND_NOTHING; + } } } } @@ -8240,7 +8248,14 @@ color_name_table_T color_name_table[] = { { NULL, 0 }, }; -RgbValue name_to_color(uint8_t *name) + +/// Translate to RgbValue if \p name is an hex value (e.g. #XXXXXX), +/// else look into color_name_table to translate a color name to its +/// hex value +/// +/// @param[in] name string value to convert to RGB +/// return the hex value or -1 if could not find a correct value +RgbValue name_to_color(const uint8_t *name) { if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h index af2ac719c6..574e3372e2 100644 --- a/src/nvim/syntax.h +++ b/src/nvim/syntax.h @@ -5,10 +5,11 @@ #include "nvim/buffer_defs.h" -/* - * Terminal highlighting attribute bits. - * Attributes above HL_ALL are used for syntax highlighting. - */ + +/// Terminal highlighting attribute bits. +/// Attributes above HL_ALL are used for syntax highlighting. +/// \addtogroup HL_ATTRIBUTES +/// @{ #define HL_NORMAL 0x00 #define HL_INVERSE 0x01 #define HL_BOLD 0x02 @@ -16,6 +17,7 @@ #define HL_UNDERLINE 0x08 #define HL_UNDERCURL 0x10 #define HL_STANDOUT 0x20 +/// @} #define HL_CONTAINED 0x01 /* not used on toplevel */ #define HL_TRANSP 0x02 /* has no highlighting */ diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 7bcaff662c..b812dd2ffd 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -674,7 +674,7 @@ do_tag ( fname = xmalloc(MAXPATHL + 1); cmd = xmalloc(CMDBUFFSIZE + 1); - list = list_alloc(); + list = tv_list_alloc(); for (i = 0; i < num_matches; ++i) { int len, cmd_len; @@ -773,20 +773,21 @@ do_tag ( cmd[len] = NUL; } - dict = dict_alloc(); - list_append_dict(list, dict); + dict = tv_dict_alloc(); + tv_list_append_dict(list, dict); - dict_add_nr_str(dict, "text", 0L, tag_name); - dict_add_nr_str(dict, "filename", 0L, fname); - dict_add_nr_str(dict, "lnum", lnum, NULL); - if (lnum == 0) - dict_add_nr_str(dict, "pattern", 0L, cmd); + tv_dict_add_str(dict, S_LEN("text"), (const char *)tag_name); + tv_dict_add_str(dict, S_LEN("filename"), (const char *)fname); + tv_dict_add_nr(dict, S_LEN("lnum"), lnum); + if (lnum == 0) { + tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cmd); + } } vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); set_errorlist(curwin, list, ' ', IObuff, NULL); - list_free(list); + tv_list_free(list); xfree(fname); xfree(cmd); @@ -2203,7 +2204,7 @@ parse_tag_line ( * Return TRUE if it is a static tag and adjust *tagname to the real tag. * Return FALSE if it is not a static tag. */ -static int test_for_static(tagptrs_T *tagp) +static bool test_for_static(tagptrs_T *tagp) { char_u *p; @@ -2768,8 +2769,8 @@ add_tag_field ( int len = 0; int retval; - /* check that the field name doesn't exist yet */ - if (dict_find(dict, (char_u *)field_name, -1) != NULL) { + // Check that the field name doesn't exist yet. + if (tv_dict_find(dict, field_name, -1) != NULL) { if (p_verbose > 0) { verbose_enter(); smsg(_("Duplicate field name: %s"), field_name); @@ -2790,7 +2791,8 @@ add_tag_field ( STRLCPY(buf, start, len + 1); } buf[len] = NUL; - retval = dict_add_nr_str(dict, field_name, 0L, buf); + retval = tv_dict_add_str(dict, field_name, STRLEN(field_name), + (const char *)buf); xfree(buf); return retval; } @@ -2806,7 +2808,7 @@ int get_tags(list_T *list, char_u *pat) char_u *full_fname; dict_T *dict; tagptrs_T tp; - long is_static; + bool is_static; ret = find_tags(pat, &num_matches, &matches, TAG_REGEXP | TAG_NOIC, (int)MAXCOL, NULL); @@ -2824,19 +2826,18 @@ int get_tags(list_T *list, char_u *pat) if (STRNCMP(tp.tagname, "!_TAG_", 6) == 0) continue; - dict = dict_alloc(); - list_append_dict(list, dict); + dict = tv_dict_alloc(); + tv_list_append_dict(list, dict); full_fname = tag_full_fname(&tp); if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL - || add_tag_field(dict, "filename", full_fname, - NULL) == FAIL - || add_tag_field(dict, "cmd", tp.command, - tp.command_end) == FAIL + || add_tag_field(dict, "filename", full_fname, NULL) == FAIL + || add_tag_field(dict, "cmd", tp.command, tp.command_end) == FAIL || add_tag_field(dict, "kind", tp.tagkind, - tp.tagkind ? tp.tagkind_end : NULL) == FAIL - || dict_add_nr_str(dict, "static", is_static, NULL) == FAIL) + tp.tagkind ? tp.tagkind_end : NULL) == FAIL + || tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) { ret = FAIL; + } xfree(full_fname); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index bd925a8106..85c4950b42 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -85,8 +85,6 @@ typedef struct terminal_state { # include "terminal.c.generated.h" #endif -#define SB_MAX 100000 // Maximum 'scrollback' value. - // Delay for refreshing the terminal buffer after receiving updates from // libvterm. Improves performance when receiving large bursts of data. #define REFRESH_DELAY 10 @@ -228,17 +226,17 @@ Terminal *terminal_open(TerminalOptions opts) rv->invalid_start = 0; rv->invalid_end = opts.height; refresh_screen(rv, curbuf); - set_option_value((uint8_t *)"buftype", 0, (uint8_t *)"terminal", OPT_LOCAL); + set_option_value("buftype", 0, "terminal", OPT_LOCAL); // Default settings for terminal buffers - curbuf->b_p_ma = false; // 'nomodifiable' - curbuf->b_p_ul = -1; // 'undolevels' - curbuf->b_p_scbk = 1000; // 'scrollback' - curbuf->b_p_tw = 0; // 'textwidth' - set_option_value((uint8_t *)"wrap", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"number", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"relativenumber", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"list", false, NULL, OPT_LOCAL); + curbuf->b_p_ma = false; // 'nomodifiable' + curbuf->b_p_ul = -1; // 'undolevels' + curbuf->b_p_scbk = p_scbk; // 'scrollback' + curbuf->b_p_tw = 0; // 'textwidth' + set_option_value("wrap", false, NULL, OPT_LOCAL); + set_option_value("number", false, NULL, OPT_LOCAL); + set_option_value("relativenumber", false, NULL, OPT_LOCAL); + set_option_value("list", false, NULL, OPT_LOCAL); buf_set_term_title(curbuf, (char *)curbuf->b_ffname); RESET_BINDING(curwin); // Reset cursor in current window. @@ -248,7 +246,8 @@ Terminal *terminal_open(TerminalOptions opts) apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf); // Configure the scrollback buffer. - rv->sb_size = curbuf->b_p_scbk < 0 ? SB_MAX : (size_t)curbuf->b_p_scbk;; + rv->sb_size = curbuf->b_p_scbk < 0 + ? SB_MAX : (size_t)MAX(1, curbuf->b_p_scbk); rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); if (!true_color) { @@ -1156,7 +1155,6 @@ static void redraw(bool restore_cursor) save_col = ui_current_col(); } block_autocmds(); - validate_cursor(); if (must_redraw) { update_screen(0); @@ -1173,7 +1171,7 @@ static void redraw(bool restore_cursor) int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1); curwin->w_cursor.col = MAX(0, term->cursor.col + win_col_off(curwin) + off); curwin->w_cursor.coladd = 0; - setcursor(); + mb_check_adjust_col(curwin); } unblock_autocmds(); @@ -1197,6 +1195,7 @@ static void adjust_topline(Terminal *term, buf_T *buf, long added) // Ensure valid cursor for each window displaying this terminal. wp->w_cursor.lnum = MIN(wp->w_cursor.lnum, ml_end); } + mb_check_adjust_col(wp); } } } diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index d090ace432..70a9f2b8c4 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -1,4 +1,4 @@ -# +# vim: noet ts=8 # Makefile to run all tests for Vim # @@ -28,17 +28,25 @@ SCRIPTS ?= \ # Tests using runtest.vim. # Keep test_alot*.res as the last one, sort the others. NEW_TESTS ?= \ + test_autocmd.res \ test_bufwintabinfo.res \ + test_charsearch.res \ test_cmdline.res \ + test_command_count.res \ test_cscope.res \ test_digraph.res \ test_diffmode.res \ test_farsi.res \ test_filter_map.res \ + test_fnameescape.res \ + test_fold.res \ + test_glob2regpat.res \ + test_gf.res \ test_gn.res \ test_hardcopy.res \ test_help_tagjump.res \ test_history.res \ + test_hlsearch.res \ test_increment.res \ test_increment_dbcs.res \ test_lambda.res \ @@ -46,13 +54,18 @@ NEW_TESTS ?= \ test_marks.res \ test_match.res \ test_matchadd_conceal.res \ + test_matchadd_conceal_utf8.res \ test_nested_function.res \ test_normal.res \ test_quickfix.res \ test_signs.res \ + test_smartindent.res \ + test_substitute.res \ test_syntax.res \ + test_tabpage.res \ test_textobjects.res \ test_timers.res \ + test_undo.res \ test_usercommands.res \ test_viml.res \ test_visual.res \ diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index b4eb9de506..1cf7ab475c 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -72,6 +72,8 @@ let v:testing = 1 set directory^=. set backspace= set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd +" Prevent Nvim log from writing to stderr. +let $NVIM_LOG_FILE='Xnvim.log' function RunTheTest(test) echo 'Executing ' . a:test diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index 05257d566d..06f2199214 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -4,8 +4,9 @@ set noruler set noshowcmd set belloff= -" Make sure 'runtimepath' does not include $HOME. +" Make sure 'runtimepath' and 'packpath' does not include $HOME. set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after +let &packpath = &rtp " Make sure $HOME does not get read or written. let $HOME = '/does/not/exist' diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim index 1138ceba4d..784e4a0a02 100644 --- a/src/nvim/testdir/shared.vim +++ b/src/nvim/testdir/shared.vim @@ -1,5 +1,114 @@ " Functions shared by several tests. +" Get the name of the Python executable. +" Also keeps it in s:python. +func PythonProg() + " This test requires the Python command to run the test server. + " This most likely only works on Unix and Windows. + if has('unix') + " We also need the job feature or the pkill command to make sure the server + " can be stopped. + if !(executable('python') && (has('job') || executable('pkill'))) + return '' + endif + let s:python = 'python' + elseif has('win32') + " Use Python Launcher for Windows (py.exe) if available. + if executable('py.exe') + let s:python = 'py.exe' + elseif executable('python.exe') + let s:python = 'python.exe' + else + return '' + endif + else + return '' + endif + return s:python +endfunc + +" Run "cmd". Returns the job if using a job. +func RunCommand(cmd) + let job = 0 + if has('job') + let job = job_start(a:cmd, {"stoponexit": "hup"}) + call job_setoptions(job, {"stoponexit": "kill"}) + elseif has('win32') + exe 'silent !start cmd /c start "test_channel" ' . a:cmd + else + exe 'silent !' . a:cmd . '&' + endif + return job +endfunc + +" Read the port number from the Xportnr file. +func GetPort() + let l = [] + for i in range(200) + try + let l = readfile("Xportnr") + catch + endtry + if len(l) >= 1 + break + endif + sleep 10m + endfor + call delete("Xportnr") + + if len(l) == 0 + " Can't make the connection, give up. + return 0 + endif + return l[0] +endfunc + +" Run a Python server for "cmd" and call "testfunc". +" Always kills the server before returning. +func RunServer(cmd, testfunc, args) + " The Python program writes the port number in Xportnr. + call delete("Xportnr") + + if len(a:args) == 1 + let arg = ' ' . a:args[0] + else + let arg = '' + endif + let pycmd = s:python . " " . a:cmd . arg + + try + let g:currentJob = RunCommand(pycmd) + + " Wait for up to 2 seconds for the port number to be there. + let port = GetPort() + if port == 0 + call assert_false(1, "Can't start " . a:cmd) + return + endif + + call call(function(a:testfunc), [port]) + catch + call assert_false(1, 'Caught exception: "' . v:exception . '" in ' . v:throwpoint) + finally + call s:kill_server(a:cmd) + endtry +endfunc + +func s:kill_server(cmd) + if has('job') + if exists('g:currentJob') + call job_stop(g:currentJob) + unlet g:currentJob + endif + elseif has('win32') + let cmd = substitute(a:cmd, ".py", '', '') + call system('taskkill /IM ' . s:python . ' /T /F /FI "WINDOWTITLE eq ' . cmd . '"') + else + call system("pkill -f " . a:cmd) + endif +endfunc + +" Wait for up to a second for "expr" to become true. " Return time slept in milliseconds. With the +reltime feature this can be " more than the actual waiting time. Without +reltime it can also be less. func WaitFor(expr) @@ -12,10 +121,10 @@ func WaitFor(expr) for i in range(100) try if eval(a:expr) - if has('reltime') - return float2nr(reltimefloat(reltime(start)) * 1000) - endif - return slept + if has('reltime') + return float2nr(reltimefloat(reltime(start)) * 1000) + endif + return slept endif catch endtry @@ -26,3 +135,80 @@ func WaitFor(expr) endfor return 1000 endfunc + +" Wait for up to a given milliseconds. +" With the +timers feature this waits for key-input by getchar(), Resume() +" feeds key-input and resumes process. Return time waited in milliseconds. +" Without +timers it uses simply :sleep. +func Standby(msec) + if has('timers') + let start = reltime() + let g:_standby_timer = timer_start(a:msec, function('s:feedkeys')) + call getchar() + return float2nr(reltimefloat(reltime(start)) * 1000) + else + execute 'sleep ' a:msec . 'm' + return a:msec + endif +endfunc + +func Resume() + if exists('g:_standby_timer') + call timer_stop(g:_standby_timer) + call s:feedkeys(0) + unlet g:_standby_timer + endif +endfunc + +func s:feedkeys(timer) + call feedkeys('x', 'nt') +endfunc + +" Get the command to run Vim, with -u NONE and --headless arguments. +" Returns an empty string on error. +func GetVimCommand() + let cmd = v:progpath + let cmd = substitute(cmd, '-u \f\+', '-u NONE', '') + if cmd !~ '-u NONE' + let cmd = cmd . ' -u NONE' + endif + let cmd .= ' --headless -i NONE' + let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '') + return cmd +endfunc + +" Run Vim, using the "vimcmd" file and "-u NORC". +" "before" is a list of Vim commands to be executed before loading plugins. +" "after" is a list of Vim commands to be executed after loading plugins. +" Plugins are not loaded, unless 'loadplugins' is set in "before". +" Return 1 if Vim could be executed. +func RunVim(before, after, arguments) + return RunVimPiped(a:before, a:after, a:arguments, '') +endfunc + +func RunVimPiped(before, after, arguments, pipecmd) + let $NVIM_LOG_FILE='Xnvim.log' + let cmd = GetVimCommand() + if cmd == '' + return 0 + endif + let args = '' + if len(a:before) > 0 + call writefile(a:before, 'Xbefore.vim') + let args .= ' --cmd "so Xbefore.vim"' + endif + if len(a:after) > 0 + call writefile(a:after, 'Xafter.vim') + let args .= ' -S Xafter.vim' + endif + + exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments + + if len(a:before) > 0 + call delete('Xbefore.vim') + endif + if len(a:after) > 0 + call delete('Xafter.vim') + endif + return 1 +endfunc diff --git a/src/nvim/testdir/test13.in b/src/nvim/testdir/test13.in deleted file mode 100644 index fa9ba312b7..0000000000 --- a/src/nvim/testdir/test13.in +++ /dev/null @@ -1,63 +0,0 @@ -Tests for autocommands on :close command - -Write three files and open them, each in a window. -Then go to next window, with autocommand that deletes the previous one. -Do this twice, writing the file. - -Also test deleting the buffer on a Unload event. If this goes wrong there -will be the ATTENTION prompt. - -Also test changing buffers in a BufDel autocommand. If this goes wrong there -are ml_line errors and/or a Crash. - -STARTTEST -:/^start of testfile/,/^end of testfile/w! Xtestje1 -:/^start of testfile/,/^end of testfile/w! Xtestje2 -:/^start of testfile/,/^end of testfile/w! Xtestje3 -:e Xtestje1 -otestje1 -:w -:sp Xtestje2 -otestje2 -:w -:sp Xtestje3 -otestje3 -:w - -:au WinLeave Xtestje2 bwipe - -:w! test.out -:au WinLeave Xtestje1 bwipe Xtestje3 -:close -:w >>test.out -:e Xtestje1 -:bwipe Xtestje2 Xtestje3 test.out -:au! -:au! BufUnload Xtestje1 bwipe -:e Xtestje3 -:w >>test.out -:e Xtestje2 -:sp Xtestje1 -:e -:w >>test.out -:au! -:only -:e Xtestje1 -:bwipe Xtestje2 Xtestje3 test.out test13.in -:au BufWipeout Xtestje1 buf Xtestje1 -:bwipe -:w >>test.out -:only -:new|set buftype=help -:wincmd w -:1quit -:$put ='Final line' -:$w >>test.out -:qa! -ENDTEST - -start of testfile - contents - contents - contents -end of testfile diff --git a/src/nvim/testdir/test13.ok b/src/nvim/testdir/test13.ok deleted file mode 100644 index 66ebce63f7..0000000000 --- a/src/nvim/testdir/test13.ok +++ /dev/null @@ -1,31 +0,0 @@ -start of testfile -testje1 - contents - contents - contents -end of testfile -start of testfile -testje1 - contents - contents - contents -end of testfile -start of testfile -testje3 - contents - contents - contents -end of testfile -start of testfile -testje2 - contents - contents - contents -end of testfile -start of testfile -testje1 - contents - contents - contents -end of testfile -Final line diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 8aa0f417d1..baf49b7ff7 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -2,7 +2,6 @@ " This makes testing go faster, since Vim doesn't need to restart. source test_assign.vim -source test_autocmd.vim source test_cursor_func.vim source test_execute_func.vim source test_ex_undo.vim @@ -13,8 +12,6 @@ source test_filter_map.vim source test_goto.vim source test_jumps.vim source test_lambda.vim -source test_match.vim -source test_matchadd_conceal_utf8.vim source test_menu.vim source test_mapping.vim source test_messages.vim @@ -26,9 +23,10 @@ source test_source_utf8.vim source test_statusline.vim source test_syn_attr.vim source test_tabline.vim -source test_tabpage.vim +" source test_tabpage.vim source test_tagcase.vim source test_tagjump.vim source test_true_false.vim source test_unlet.vim +source test_utf8.vim source test_window_cmd.vim diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index f05a55f1aa..c4110eba94 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1,5 +1,15 @@ " Tests for autocommands +set belloff=all + +function! s:cleanup_buffers() abort + for bnr in range(1, bufnr('$')) + if bufloaded(bnr) && bufnr('%') != bnr + execute 'bd! ' . bnr + endif + endfor +endfunction + func Test_vim_did_enter() call assert_false(v:vim_did_enter) @@ -13,6 +23,9 @@ if has('timers') endfunc func Test_cursorhold_insert() + " Need to move the cursor. + call feedkeys("ggG", "xt") + let g:triggered = 0 au CursorHoldI * let g:triggered += 1 set updatetime=20 @@ -20,6 +33,7 @@ if has('timers') call feedkeys('a', 'x!') call assert_equal(1, g:triggered) au! CursorHoldI + set updatetime& endfunc func Test_cursorhold_insert_ctrl_x() @@ -31,6 +45,7 @@ if has('timers') call feedkeys("a\<C-X>", 'x!') call assert_equal(0, g:triggered) au! CursorHoldI + set updatetime& endfunc endif @@ -77,11 +92,60 @@ function Test_autocmd_bufunload_with_tabnext() quit call assert_equal(2, tabpagenr('$')) + autocmd! test_autocmd_bufunload_with_tabnext_group augroup! test_autocmd_bufunload_with_tabnext_group tablast quit endfunc +function Test_autocmd_bufwinleave_with_tabfirst() + tabedit + augroup sample + autocmd! + autocmd BufWinLeave <buffer> tabfirst + augroup END + call setline(1, ['a', 'b', 'c']) + edit! a.txt + tabclose +endfunc + +" SEGV occurs in older versions. (At least 7.4.2321 or older) +function Test_autocmd_bufunload_avoiding_SEGV_01() + split aa.txt + let lastbuf = bufnr('$') + + augroup test_autocmd_bufunload + autocmd! + exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!' + augroup END + + call assert_fails('edit bb.txt', 'E937:') + + autocmd! test_autocmd_bufunload + augroup! test_autocmd_bufunload + bwipe! aa.txt + bwipe! bb.txt +endfunc + +" SEGV occurs in older versions. (At least 7.4.2321 or older) +function Test_autocmd_bufunload_avoiding_SEGV_02() + setlocal buftype=nowrite + let lastbuf = bufnr('$') + + augroup test_autocmd_bufunload + autocmd! + exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!' + augroup END + + normal! i1 + call assert_fails('edit a.txt', 'E517:') + call feedkeys("\<CR>") + + autocmd! test_autocmd_bufunload + augroup! test_autocmd_bufunload + bwipe! a.txt +endfunc + func Test_win_tab_autocmd() let g:record = [] @@ -172,6 +236,7 @@ func Test_augroup_warning() augroup Another augroup END call assert_true(match(execute('au VimEnter'), "-Deleted-.*VimEnter") >= 0) + augroup! Another " no warning for postpone aucmd delete augroup StartOK @@ -196,3 +261,160 @@ func Test_augroup_deleted() au! VimEnter endfunc +" Tests for autocommands on :close command. +" This used to be in test13. +func Test_three_windows() + " Clean up buffers, because in some cases this function fails. + call s:cleanup_buffers() + + " Write three files and open them, each in a window. + " Then go to next window, with autocommand that deletes the previous one. + " Do this twice, writing the file. + e! Xtestje1 + call setline(1, 'testje1') + w + sp Xtestje2 + call setline(1, 'testje2') + w + sp Xtestje3 + call setline(1, 'testje3') + w + wincmd w + au WinLeave Xtestje2 bwipe + wincmd w + call assert_equal('Xtestje1', expand('%')) + + au WinLeave Xtestje1 bwipe Xtestje3 + close + call assert_equal('Xtestje1', expand('%')) + + " Test deleting the buffer on a Unload event. If this goes wrong there + " will be the ATTENTION prompt. + e Xtestje1 + au! + au! BufUnload Xtestje1 bwipe + call assert_fails('e Xtestje3', 'E937:') + call assert_equal('Xtestje3', expand('%')) + + e Xtestje2 + sp Xtestje1 + call assert_fails('e', 'E937:') + call assert_equal('Xtestje2', expand('%')) + + " Test changing buffers in a BufWipeout autocommand. If this goes wrong + " there are ml_line errors and/or a Crash. + au! + only + e Xanother + e Xtestje1 + bwipe Xtestje2 + bwipe Xtestje3 + au BufWipeout Xtestje1 buf Xtestje1 + bwipe + call assert_equal('Xanother', expand('%')) + + only + + helptags ALL + help + wincmd w + 1quit + call assert_equal('Xanother', expand('%')) + + au! + enew + bwipe! Xtestje1 + call delete('Xtestje1') + call delete('Xtestje2') + call delete('Xtestje3') +endfunc + +func Test_BufEnter() + au! BufEnter + au Bufenter * let val = val . '+' + let g:val = '' + split NewFile + call assert_equal('+', g:val) + bwipe! + call assert_equal('++', g:val) + + " Also get BufEnter when editing a directory + call mkdir('Xdir') + split Xdir + call assert_equal('+++', g:val) + bwipe! + + call delete('Xdir', 'd') + au! BufEnter +endfunc + +" Closing a window might cause an endless loop +" E814 for older Vims +function Test_autocmd_bufwipe_in_SessLoadPost() + if has('win32') + throw 'Skipped: test hangs on MS-Windows' + endif + tabnew + set noswapfile + let g:bufnr=bufnr('%') + mksession! + + let content=['set nocp noswapfile', + \ 'let v:swapchoice="e"', + \ 'augroup test_autocmd_sessionload', + \ 'autocmd!', + \ 'autocmd SessionLoadPost * 4bw!|qall!', + \ 'augroup END', + \ ] + call writefile(content, 'Xvimrc') + let a=system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim') + call assert_match('E814', a) + + unlet! g:bufnr + set swapfile + for file in ['Session.vim', 'Xvimrc'] + call delete(file) + endfor +endfunc + +" SEGV occurs in older versions. +function Test_autocmd_bufwipe_in_SessLoadPost2() + if has('win32') + throw 'Skipped: test hangs on MS-Windows' + endif + tabnew + set noswapfile + let g:bufnr=bufnr('%') + mksession! + + let content = ['set nocp noswapfile', + \ 'function! DeleteInactiveBufs()', + \ ' tabfirst', + \ ' let tabblist = []', + \ ' for i in range(1, tabpagenr(''$''))', + \ ' call extend(tabblist, tabpagebuflist(i))', + \ ' endfor', + \ ' for b in range(1, bufnr(''$''))', + \ ' if bufexists(b) && buflisted(b) && (index(tabblist, b) == -1 || bufname(b) =~# ''^$'')', + \ ' exec ''bwipeout '' . b', + \ ' endif', + \ ' endfor', + \ 'redraw!', + \ 'echon "SessionLoadPost DONE"', + \ 'qall!', + \ 'endfunction', + \ 'au SessionLoadPost * call DeleteInactiveBufs()'] + call writefile(content, 'Xvimrc') + let a=system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim') + " this probably only matches on unix + if has("unix") + call assert_notmatch('Caught deadly signal SEGV', a) + endif + call assert_match('SessionLoadPost DONE', a) + + unlet! g:bufnr + set swapfile + for file in ['Session.vim', 'Xvimrc'] + call delete(file) + endfor +endfunc diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim new file mode 100644 index 0000000000..8b313b5a35 --- /dev/null +++ b/src/nvim/testdir/test_charsearch.vim @@ -0,0 +1,62 @@ + +function! Test_charsearch() + enew! + call append(0, ['Xabcdefghijkemnopqretuvwxyz', + \ 'Yabcdefghijkemnopqretuvwxyz', + \ 'Zabcdefghijkemnokqretkvwxyz']) + " check that "fe" and ";" work + 1 + normal! ylfep;;p,,p + call assert_equal('XabcdeXfghijkeXmnopqreXtuvwxyz', getline(1)) + " check that save/restore works + 2 + normal! ylfep + let csave = getcharsearch() + normal! fip + call setcharsearch(csave) + normal! ;p;p + call assert_equal('YabcdeYfghiYjkeYmnopqreYtuvwxyz', getline(2)) + + " check that setcharsearch() changes the settings. + 3 + normal! ylfep + call setcharsearch({'char': 'k'}) + normal! ;p + call setcharsearch({'forward': 0}) + normal! $;p + call setcharsearch({'until': 1}) + set cpo-=; + normal! ;;p + call assert_equal('ZabcdeZfghijkZZemnokqretkZvwxyz', getline(3)) + enew! +endfunction + +" Test for t,f,F,T movement commands and 'cpo-;' setting +function! Test_search_cmds() + enew! + call append(0, ["aaa two three four", " zzz", "yyy ", + \ "bbb yee yoo four", "ccc two three four", + \ "ddd yee yoo four"]) + set cpo-=; + 1 + normal! 0tt;D + 2 + normal! 0fz;D + 3 + normal! $Fy;D + 4 + normal! $Ty;D + set cpo+=; + 5 + normal! 0tt;;D + 6 + normal! $Ty;;D + + call assert_equal('aaa two', getline(1)) + call assert_equal(' z', getline(2)) + call assert_equal('y', getline(3)) + call assert_equal('bbb y', getline(4)) + call assert_equal('ccc', getline(5)) + call assert_equal('ddd yee y', getline(6)) + enew! +endfunction diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 40db227d97..0c9d1297d6 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -156,6 +156,20 @@ func Test_getcompletion() call assert_equal(['Testing'], l) endif + " Command line completion tests + let l = getcompletion('cd ', 'cmdline') + call assert_true(index(l, 'sautest/') >= 0) + let l = getcompletion('cd NoMatch', 'cmdline') + call assert_equal([], l) + let l = getcompletion('let v:n', 'cmdline') + call assert_true(index(l, 'v:null') >= 0) + let l = getcompletion('let v:notexists', 'cmdline') + call assert_equal([], l) + let l = getcompletion('call tag', 'cmdline') + call assert_true(index(l, 'taglist(') >= 0) + let l = getcompletion('call paint', 'cmdline') + call assert_equal([], l) + " For others test if the name is recognized. let names = ['buffer', 'environment', 'file_in_path', \ 'mapping', 'shellcmd', 'tag', 'tag_listfiles', 'user'] @@ -187,5 +201,6 @@ func Test_expand_star_star() call writefile(['asdfasdf'], 'a/b/fileXname') call feedkeys(":find **/fileXname\<Tab>\<CR>", 'xt') call assert_equal('find a/b/fileXname', getreg(':')) + bwipe! call delete('a', 'rf') endfunc diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim new file mode 100644 index 0000000000..e438a8b077 --- /dev/null +++ b/src/nvim/testdir/test_command_count.vim @@ -0,0 +1,191 @@ +" Test for user command counts. + +func Test_command_count_0() + set hidden + set noswapfile + + split DoesNotExistEver + let lastbuf = bufnr('$') + call setline(1, 'asdf') + quit! + + command! -range -addr=loaded_buffers RangeLoadedBuffers :let lines = [<line1>, <line2>] + command! -range=% -addr=loaded_buffers RangeLoadedBuffersAll :let lines = [<line1>, <line2>] + command! -range -addr=buffers RangeBuffers :let lines = [<line1>, <line2>] + command! -range=% -addr=buffers RangeBuffersAll :let lines = [<line1>, <line2>] + + .,$RangeLoadedBuffers + call assert_equal([1, 1], lines) + %RangeLoadedBuffers + call assert_equal([1, 1], lines) + RangeLoadedBuffersAll + call assert_equal([1, 1], lines) + .,$RangeBuffers + call assert_equal([1, lastbuf], lines) + %RangeBuffers + call assert_equal([1, lastbuf], lines) + RangeBuffersAll + call assert_equal([1, lastbuf], lines) + + delcommand RangeLoadedBuffers + delcommand RangeLoadedBuffersAll + delcommand RangeBuffers + delcommand RangeBuffersAll + + set hidden& + set swapfile& +endfunc + +func Test_command_count_1() + silent! %argd + arga a b c d e + argdo echo "loading buffers" + argu 3 + command! -range -addr=arguments RangeArguments :let lines = [<line1>, <line2>] + command! -range=% -addr=arguments RangeArgumentsAll :let lines = [<line1>, <line2>] + .-,$-RangeArguments + call assert_equal([2, 4], lines) + %RangeArguments + call assert_equal([1, 5], lines) + RangeArgumentsAll + call assert_equal([1, 5], lines) + N + .RangeArguments + call assert_equal([2, 2], lines) + delcommand RangeArguments + delcommand RangeArgumentsAll + + split|split|split|split + 3wincmd w + command! -range -addr=windows RangeWindows :let lines = [<line1>, <line2>] + .,$RangeWindows + call assert_equal([3, 5], lines) + %RangeWindows + call assert_equal([1, 5], lines) + delcommand RangeWindows + + command! -range=% -addr=windows RangeWindowsAll :let lines = [<line1>, <line2>] + RangeWindowsAll + call assert_equal([1, 5], lines) + delcommand RangeWindowsAll + only + blast|bd + + tabe|tabe|tabe|tabe + normal 2gt + command! -range -addr=tabs RangeTabs :let lines = [<line1>, <line2>] + .,$RangeTabs + call assert_equal([2, 5], lines) + %RangeTabs + call assert_equal([1, 5], lines) + delcommand RangeTabs + + command! -range=% -addr=tabs RangeTabsAll :let lines = [<line1>, <line2>] + RangeTabsAll + call assert_equal([1, 5], lines) + delcommand RangeTabsAll + 1tabonly + + s/\n/\r\r\r\r\r/ + 2ma< + $-ma> + command! -range=% RangeLines :let lines = [<line1>, <line2>] + '<,'>RangeLines + call assert_equal([2, 5], lines) + delcommand RangeLines + + command! -range=% -buffer LocalRangeLines :let lines = [<line1>, <line2>] + '<,'>LocalRangeLines + call assert_equal([2, 5], lines) + delcommand LocalRangeLines +endfunc + +func Test_command_count_2() + silent! %argd + arga a b c d + call assert_fails('5argu', 'E16:') + + $argu + call assert_equal('d', expand('%:t')) + + 1argu + call assert_equal('a', expand('%:t')) + + call assert_fails('300b', 'E16:') + + split|split|split|split + 0close + + $wincmd w + $close + call assert_equal(3, winnr()) + + call assert_fails('$+close', 'E16:') + + $tabe + call assert_equal(2, tabpagenr()) + + call assert_fails('$+tabe', 'E16:') + + only! + e x + 0tabm + normal 1gt + call assert_equal('x', expand('%:t')) + + tabonly! + only! +endfunc + +func Test_command_count_3() + se nohidden + e aaa + let buf_aaa = bufnr('%') + e bbb + let buf_bbb = bufnr('%') + e ccc + let buf_ccc = bufnr('%') + buf 1 + call assert_equal([1, 1, 1], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)]) + exe buf_bbb . "," . buf_ccc . "bdelete" + call assert_equal([1, 0, 0], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)]) + exe buf_aaa . "bdelete" + call assert_equal([0, 0, 0], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)]) +endfunc + +func Test_command_count_4() + %argd + let bufnr = bufnr('$') + 1 + arga aa bb cc dd ee ff + 3argu + let args = [] + .,$-argdo call add(args, expand('%')) + call assert_equal(['cc', 'dd', 'ee'], args) + + " create windows to get 5 + split|split|split|split + 2wincmd w + let windows = [] + .,$-windo call add(windows, winnr()) + call assert_equal([2, 3, 4], windows) + only! + + exe bufnr . 'buf' + let buffers = [] + .,$-bufdo call add(buffers, bufnr('%')) + call assert_equal([bufnr, bufnr + 1, bufnr + 2, bufnr + 3, bufnr + 4], buffers) + + exe (bufnr + 3) . 'bdel' + let buffers = [] + exe (bufnr + 2) . ',' . (bufnr + 5) . "bufdo call add(buffers, bufnr('%'))" + call assert_equal([bufnr + 2, bufnr + 4, bufnr + 5], buffers) + + " create tabpages to get 5 + tabe|tabe|tabe|tabe + normal! 2gt + let tabpages = [] + .,$-tabdo call add(tabpages, tabpagenr()) + call assert_equal([2, 3, 4], tabpages) + tabonly! + bwipe! +endfunc diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 7f7811dc7a..03a9155512 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -101,8 +101,12 @@ endfunc func Test_setmatches() hi def link 1 Comment hi def link 2 PreProc - let set = [{"group": 1, "pattern": 2, "id": 3, "priority": 4, "conceal": 5}] - let exp = [{"group": '1', "pattern": '2', "id": 3, "priority": 4, "conceal": '5'}] + let set = [{"group": 1, "pattern": 2, "id": 3, "priority": 4}] + let exp = [{"group": '1', "pattern": '2', "id": 3, "priority": 4}] + if has('conceal') + let set[0]['conceal'] = 5 + let exp[0]['conceal'] = '5' + endif call setmatches(set) call assert_equal(exp, getmatches()) endfunc @@ -152,20 +156,52 @@ function Test_printf_misc() call assert_equal(' 123', printf('% d', 123)) call assert_equal('-123', printf('% d', -123)) + call assert_equal('123', printf('%2d', 123)) + call assert_equal(' 123', printf('%6d', 123)) + call assert_equal('000123', printf('%06d', 123)) + call assert_equal('+00123', printf('%+06d', 123)) + call assert_equal(' 00123', printf('% 06d', 123)) + call assert_equal(' +123', printf('%+6d', 123)) + call assert_equal(' 123', printf('% 6d', 123)) + call assert_equal(' -123', printf('% 6d', -123)) + + " Test left adjusted. + call assert_equal('123 ', printf('%-6d', 123)) + call assert_equal('+123 ', printf('%-+6d', 123)) + call assert_equal(' 123 ', printf('%- 6d', 123)) + call assert_equal('-123 ', printf('%- 6d', -123)) + + call assert_equal(' 00123', printf('%7.5d', 123)) + call assert_equal(' -00123', printf('%7.5d', -123)) + call assert_equal(' +00123', printf('%+7.5d', 123)) + " Precision field should not be used when combined with %0 + call assert_equal(' 00123', printf('%07.5d', 123)) + call assert_equal(' -00123', printf('%07.5d', -123)) + + call assert_equal(' 123', printf('%*d', 5, 123)) + call assert_equal('123 ', printf('%*d', -5, 123)) call assert_equal('00123', printf('%.*d', 5, 123)) call assert_equal(' 123', printf('% *d', 5, 123)) call assert_equal(' +123', printf('%+ *d', 5, 123)) - call assert_equal('123', printf('%2d', 123)) - call assert_equal(' 123', printf('%5d', 123)) - call assert_equal('00123', printf('%05d', 123)) - call assert_equal('123 ', printf('%-5d', 123)) + " Simple quote (thousand grouping char) is ignored. + call assert_equal('+00123456', printf("%+'09d", 123456)) + + " Unrecognized format specifier kept as-is. + call assert_equal('_123', printf("%_%d", 123)) + + " Test alternate forms. call assert_equal('0x7b', printf('%#x', 123)) call assert_equal('0X7B', printf('%#X', 123)) call assert_equal('0173', printf('%#o', 123)) call assert_equal('0173', printf('%#O', 123)) call assert_equal('abc', printf('%#s', 'abc')) call assert_equal('abc', printf('%#S', 'abc')) + call assert_equal(' 0173', printf('%#6o', 123)) + call assert_equal(' 00173', printf('%#6.5o', 123)) + call assert_equal(' 0173', printf('%#6.2o', 123)) + call assert_equal(' 0173', printf('%#6.2o', 123)) + call assert_equal('0173', printf('%#2.2o', 123)) call assert_equal(' 00123', printf('%6.5d', 123)) call assert_equal(' 0007b', printf('%6.5x', 123)) @@ -189,24 +225,104 @@ function Test_printf_misc() endfunc function Test_printf_float() + call assert_equal('1.000000', printf('%f', 1)) call assert_equal('1.230000', printf('%f', 1.23)) call assert_equal('1.230000', printf('%F', 1.23)) - call assert_equal('1.23', printf('%g', 1.23)) - call assert_equal('1.23', printf('%G', 1.23)) + call assert_equal('9999999.9', printf('%g', 9999999.9)) + call assert_equal('9999999.9', printf('%G', 9999999.9)) + call assert_equal('1.00000001e7', printf('%.8g', 10000000.1)) + call assert_equal('1.00000001E7', printf('%.8G', 10000000.1)) call assert_equal('1.230000e+00', printf('%e', 1.23)) call assert_equal('1.230000E+00', printf('%E', 1.23)) call assert_equal('1.200000e-02', printf('%e', 0.012)) call assert_equal('-1.200000e-02', printf('%e', -0.012)) - call assert_equal('1.2', printf('%.1f', 1.23)) - + call assert_equal('0.33', printf('%.2f', 1.0/3.0)) + call assert_equal(' 0.33', printf('%6.2f', 1.0/3.0)) + call assert_equal(' -0.33', printf('%6.2f', -1.0/3.0)) + call assert_equal('000.33', printf('%06.2f', 1.0/3.0)) + call assert_equal('-00.33', printf('%06.2f', -1.0/3.0)) + call assert_equal('-00.33', printf('%+06.2f', -1.0/3.0)) + call assert_equal('+00.33', printf('%+06.2f', 1.0/3.0)) + call assert_equal(' 00.33', printf('% 06.2f', 1.0/3.0)) + call assert_equal('000.33', printf('%06.2g', 1.0/3.0)) + call assert_equal('-00.33', printf('%06.2g', -1.0/3.0)) + call assert_equal('0.33', printf('%3.2f', 1.0/3.0)) + call assert_equal('003.33e-01', printf('%010.2e', 1.0/3.0)) + call assert_equal(' 03.33e-01', printf('% 010.2e', 1.0/3.0)) + call assert_equal('+03.33e-01', printf('%+010.2e', 1.0/3.0)) + call assert_equal('-03.33e-01', printf('%010.2e', -1.0/3.0)) + + " When precision is 0, the dot should be omitted. + call assert_equal(' 2', printf('%3.f', 7.0/3.0)) + call assert_equal(' 2', printf('%3.g', 7.0/3.0)) + call assert_equal(' 2e+00', printf('%7.e', 7.0/3.0)) + + " Float zero can be signed. + call assert_equal('+0.000000', printf('%+f', 0.0)) + call assert_equal('0.000000', printf('%f', 1.0/(1.0/0.0))) + call assert_equal('-0.000000', printf('%f', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%s', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%s', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%S', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%S', 1.0/(-1.0/0.0))) + + " Float infinity can be signed. call assert_equal('inf', printf('%f', 1.0/0.0)) - - " This prints inf but shouldn't it print -inf instead? - call assert_match('^-\?inf$', printf('%f', -1.0/0.0)) - - " This prints -nan but shouldn't it print nan instead? - call assert_match('^-\?nan$', printf('%f', sqrt(-1.0))) - call assert_match('^-\?nan$', printf('%f', 0.0/0.0)) + call assert_equal('-inf', printf('%f', -1.0/0.0)) + call assert_equal('inf', printf('%g', 1.0/0.0)) + call assert_equal('-inf', printf('%g', -1.0/0.0)) + call assert_equal('inf', printf('%e', 1.0/0.0)) + call assert_equal('-inf', printf('%e', -1.0/0.0)) + call assert_equal('INF', printf('%F', 1.0/0.0)) + call assert_equal('-INF', printf('%F', -1.0/0.0)) + call assert_equal('INF', printf('%E', 1.0/0.0)) + call assert_equal('-INF', printf('%E', -1.0/0.0)) + call assert_equal('INF', printf('%E', 1.0/0.0)) + call assert_equal('-INF', printf('%G', -1.0/0.0)) + call assert_equal('+inf', printf('%+f', 1.0/0.0)) + call assert_equal('-inf', printf('%+f', -1.0/0.0)) + call assert_equal(' inf', printf('% f', 1.0/0.0)) + call assert_equal(' inf', printf('%6f', 1.0/0.0)) + call assert_equal(' -inf', printf('%6f', -1.0/0.0)) + call assert_equal(' inf', printf('%6g', 1.0/0.0)) + call assert_equal(' -inf', printf('%6g', -1.0/0.0)) + call assert_equal(' +inf', printf('%+6f', 1.0/0.0)) + call assert_equal(' inf', printf('% 6f', 1.0/0.0)) + call assert_equal(' +inf', printf('%+06f', 1.0/0.0)) + call assert_equal('inf ', printf('%-6f', 1.0/0.0)) + call assert_equal('-inf ', printf('%-6f', -1.0/0.0)) + call assert_equal('+inf ', printf('%-+6f', 1.0/0.0)) + call assert_equal(' inf ', printf('%- 6f', 1.0/0.0)) + call assert_equal('-INF ', printf('%-6F', -1.0/0.0)) + call assert_equal('+INF ', printf('%-+6F', 1.0/0.0)) + call assert_equal(' INF ', printf('%- 6F', 1.0/0.0)) + call assert_equal('INF ', printf('%-6G', 1.0/0.0)) + call assert_equal('-INF ', printf('%-6G', -1.0/0.0)) + call assert_equal('INF ', printf('%-6E', 1.0/0.0)) + call assert_equal('-INF ', printf('%-6E', -1.0/0.0)) + call assert_equal("str2float('inf')", printf('%s', 1.0/0.0)) + call assert_equal("-str2float('inf')", printf('%s', -1.0/0.0)) + + " Float nan (not a number) has no sign. + call assert_equal('nan', printf('%f', sqrt(-1.0))) + call assert_equal('nan', printf('%f', 0.0/0.0)) + call assert_equal('nan', printf('%f', -0.0/0.0)) + call assert_equal('nan', printf('%g', 0.0/0.0)) + call assert_equal('nan', printf('%e', 0.0/0.0)) + call assert_equal('NAN', printf('%F', 0.0/0.0)) + call assert_equal('NAN', printf('%G', 0.0/0.0)) + call assert_equal('NAN', printf('%E', 0.0/0.0)) + call assert_equal('NAN', printf('%F', -0.0/0.0)) + call assert_equal('NAN', printf('%G', -0.0/0.0)) + call assert_equal('NAN', printf('%E', -0.0/0.0)) + call assert_equal(' nan', printf('%6f', 0.0/0.0)) + call assert_equal(' nan', printf('%06f', 0.0/0.0)) + call assert_equal('nan ', printf('%-6f', 0.0/0.0)) + call assert_equal('nan ', printf('%- 6f', 0.0/0.0)) + call assert_equal("str2float('nan')", printf('%s', 0.0/0.0)) + call assert_equal("str2float('nan')", printf('%s', -0.0/0.0)) + call assert_equal("str2float('nan')", printf('%S', 0.0/0.0)) + call assert_equal("str2float('nan')", printf('%S', -0.0/0.0)) call assert_fails('echo printf("%f", "a")', 'E807:') endfunc @@ -219,6 +335,13 @@ function Test_printf_errors() call assert_fails('echo printf("%d", 1.2)', 'E805:') endfunc +function Test_max_min_errors() + call assert_fails('call max(v:true)', 'E712:') + call assert_fails('call max(v:true)', 'max()') + call assert_fails('call min(v:true)', 'E712:') + call assert_fails('call min(v:true)', 'min()') +endfunc + func Test_substitute_expr() let g:val = 'XXX' call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', '')) diff --git a/src/nvim/testdir/test_fnameescape.vim b/src/nvim/testdir/test_fnameescape.vim new file mode 100644 index 0000000000..cdff0dfbd9 --- /dev/null +++ b/src/nvim/testdir/test_fnameescape.vim @@ -0,0 +1,21 @@ + +" Test if fnameescape is correct for special chars like ! +function! Test_fnameescape() + let fname = 'Xspa ce' + let status = v:false + try + exe "w! " . fnameescape(fname) + let status = v:true + endtry + call assert_true(status, "Space") + call delete(fname) + + let fname = 'Xemark!' + let status = v:false + try + exe "w! " . fnameescape(fname) + let status = v:true + endtry + call assert_true(status, "ExclamationMark") + call delete(fname) +endfunction diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 7cb9faa75f..46c54e8614 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -1,5 +1,9 @@ " Test for folding +func! PrepIndent(arg) + return [a:arg] + repeat(["\t".a:arg], 5) +endfu + func! Test_address_fold() new call setline(1, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/', @@ -96,6 +100,78 @@ func! Test_indent_fold2() bw! endfunc +func Test_manual_fold_with_filter() + if !executable('cat') + return + endif + for type in ['manual', 'marker'] + exe 'set foldmethod=' . type + new + call setline(1, range(1, 20)) + 4,$fold + %foldopen + 10,$fold + %foldopen + " This filter command should not have an effect + 1,8! cat + call feedkeys('5ggzdzMGdd', 'xt') + call assert_equal(['1', '2', '3', '4', '5', '6', '7', '8', '9'], getline(1, '$')) + + bwipe! + set foldmethod& + endfor +endfunc + +func! Test_indent_fold_with_read() + new + set foldmethod=indent + call setline(1, repeat(["\<Tab>a"], 4)) + for n in range(1, 4) + call assert_equal(1, foldlevel(n)) + endfor + + call writefile(["a", "", "\<Tab>a"], 'Xfile') + foldopen + 2read Xfile + %foldclose + call assert_equal(1, foldlevel(1)) + call assert_equal(2, foldclosedend(1)) + call assert_equal(0, foldlevel(3)) + call assert_equal(0, foldlevel(4)) + call assert_equal(1, foldlevel(5)) + call assert_equal(7, foldclosedend(5)) + + bwipe! + set foldmethod& + call delete('Xfile') +endfunc + +func Test_combining_folds_indent() + new + let one = "\<Tab>a" + let zero = 'a' + call setline(1, [one, one, zero, zero, zero, one, one, one]) + set foldmethod=indent + 3,5d + %foldclose + call assert_equal(5, foldclosedend(1)) + + set foldmethod& + bwipe! +endfunc + +func Test_combining_folds_marker() + new + call setline(1, ['{{{', '}}}', '', '', '', '{{{', '', '}}}']) + set foldmethod=marker + 3,5d + %foldclose + call assert_equal(2, foldclosedend(1)) + + set foldmethod& + bwipe! +endfunc + func Test_folds_marker_in_comment() new call setline(1, ['" foo', 'bar', 'baz']) @@ -112,19 +188,175 @@ func Test_folds_marker_in_comment() bwipe! endfunc -func Test_manual_fold_with_filter() - if !executable('cat') - return +func s:TestFoldExpr(lnum) + let thisline = getline(a:lnum) + if thisline == 'a' + return 1 + elseif thisline == 'b' + return 0 + elseif thisline == 'c' + return '<1' + elseif thisline == 'd' + return '>1' endif + return 0 +endfunction + +func Test_update_folds_expr_read() new - call setline(1, range(1, 20)) - 4,$fold + call setline(1, ['a', 'a', 'a', 'a', 'a', 'a']) + set foldmethod=expr + set foldexpr=s:TestFoldExpr(v:lnum) + 2 + foldopen + call writefile(['b', 'b', 'a', 'a', 'd', 'a', 'a', 'c'], 'Xfile') + read Xfile + %foldclose + call assert_equal(2, foldclosedend(1)) + call assert_equal(0, foldlevel(3)) + call assert_equal(0, foldlevel(4)) + call assert_equal(6, foldclosedend(5)) + call assert_equal(10, foldclosedend(7)) + call assert_equal(14, foldclosedend(11)) + + call delete('Xfile') + bwipe! + set foldmethod& foldexpr& +endfunc + +func! Test_move_folds_around_manual() + new + let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c") + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + let folds=[-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14] + " all folds closed + set foldenable foldlevel=0 fdm=indent + " needs a forced redraw + redraw! + set fdm=manual + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + call assert_equal(input, getline(1, '$')) + 7,12m0 + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + 10,12m0 + call assert_equal(PrepIndent("a")[1:] + PrepIndent("b") + ["a"] + PrepIndent("c"), getline(1, '$')) + call assert_equal([1, 1, 1, 1, 1, -1, 7, 7, 7, 7, 7, -1, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + " moving should not close the folds + %d + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + set fdm=indent + redraw! + set fdm=manual + call cursor(2, 1) %foldopen - 10,$fold + 7,12m0 + let folds=repeat([-1], 18) + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + norm! zM + " folds are not corrupted and all have been closed + call assert_equal([-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te"]) + set fdm=indent + redraw! + set fdm=manual %foldopen - " This filter command should not have an effect - 1,8! cat - call feedkeys('5ggzdzMGdd', 'xt') - call assert_equal(['1', '2', '3', '4', '5', '6', '7', '8', '9'], getline(1, '$')) - bwipe! + 3m4 + %foldclose + call assert_equal(["a", "\tb", "\td", "\tc", "\te"], getline(1, '$')) + call assert_equal([-1, 5, 5, 5, 5], map(range(1, line('$')), 'foldclosedend(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"]) + set fdm=indent foldlevel=0 + set fdm=manual + %foldopen + 3m1 + %foldclose + call assert_equal(["a", "\tc", "\tb", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"], getline(1, '$')) + call assert_equal(0, foldlevel(2)) + call assert_equal(5, foldclosedend(3)) + call assert_equal([-1, -1, 3, 3, 3, -1, 7, 7, 7, 7], map(range(1, line('$')), 'foldclosed(v:val)')) + 2,6m$ + %foldclose + call assert_equal(5, foldclosedend(2)) + call assert_equal(0, foldlevel(6)) + call assert_equal(9, foldclosedend(7)) + call assert_equal([-1, 2, 2, 2, 2, -1, 7, 7, 7, -1], map(range(1, line('$')), 'foldclosed(v:val)')) + %d + " Ensure moving around the edges still works. + call setline(1, PrepIndent("a") + repeat(["a"], 3) + ["\ta"]) + set fdm=indent foldlevel=0 + set fdm=manual + %foldopen + 6m$ + " The first fold has been truncated to the 5'th line. + " Second fold has been moved up because the moved line is now below it. + call assert_equal([0, 1, 1, 1, 1, 0, 0, 0, 1, 0], map(range(1, line('$')), 'foldlevel(v:val)')) + bw! +endfunc + +func! Test_move_folds_around_indent() + new + let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c") + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + let folds=[-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14] + " all folds closed + set fdm=indent + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + call assert_equal(input, getline(1, '$')) + 7,12m0 + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + 10,12m0 + call assert_equal(PrepIndent("a")[1:] + PrepIndent("b") + ["a"] + PrepIndent("c"), getline(1, '$')) + call assert_equal([1, 1, 1, 1, 1, -1, 7, 7, 7, 7, 7, -1, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + " moving should not close the folds + %d + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + set fdm=indent + call cursor(2, 1) + %foldopen + 7,12m0 + let folds=repeat([-1], 18) + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + norm! zM + " folds are not corrupted and all have been closed + call assert_equal([-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te"]) + set fdm=indent + %foldopen + 3m4 + %foldclose + call assert_equal(["a", "\tb", "\td", "\tc", "\te"], getline(1, '$')) + call assert_equal([-1, 5, 5, 5, 5], map(range(1, line('$')), 'foldclosedend(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"]) + set fdm=indent foldlevel=0 + %foldopen + 3m1 + %foldclose + call assert_equal(["a", "\tc", "\tb", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"], getline(1, '$')) + call assert_equal(1, foldlevel(2)) + call assert_equal(5, foldclosedend(3)) + call assert_equal([-1, 2, 2, 2, 2, -1, 7, 7, 7, 7], map(range(1, line('$')), 'foldclosed(v:val)')) + 2,6m$ + %foldclose + call assert_equal(9, foldclosedend(2)) + call assert_equal(1, foldlevel(6)) + call assert_equal(9, foldclosedend(7)) + call assert_equal([-1, 2, 2, 2, 2, 2, 2, 2, 2, -1], map(range(1, line('$')), 'foldclosed(v:val)')) + " Ensure moving around the edges still works. + %d + call setline(1, PrepIndent("a") + repeat(["a"], 3) + ["\ta"]) + set fdm=indent foldlevel=0 + %foldopen + 6m$ + " The first fold has been truncated to the 5'th line. + " Second fold has been moved up because the moved line is now below it. + call assert_equal([0, 1, 1, 1, 1, 0, 0, 0, 1, 1], map(range(1, line('$')), 'foldlevel(v:val)')) + bw! endfunc diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim new file mode 100644 index 0000000000..81cb6314ce --- /dev/null +++ b/src/nvim/testdir/test_functions.vim @@ -0,0 +1,31 @@ +func Test_setbufvar_options() + " This tests that aucmd_prepbuf() and aucmd_restbuf() properly restore the + " window layout. + call assert_equal(1, winnr('$')) + split dummy_preview + resize 2 + set winfixheight winfixwidth + let prev_id = win_getid() + + wincmd j + let wh = winheight('.') + let dummy_buf = bufnr('dummy_buf1', v:true) + call setbufvar(dummy_buf, '&buftype', 'nofile') + execute 'belowright vertical split #' . dummy_buf + call assert_equal(wh, winheight('.')) + let dum1_id = win_getid() + + wincmd h + let wh = winheight('.') + let dummy_buf = bufnr('dummy_buf2', v:true) + call setbufvar(dummy_buf, '&buftype', 'nofile') + execute 'belowright vertical split #' . dummy_buf + call assert_equal(wh, winheight('.')) + + bwipe! + call win_gotoid(prev_id) + bwipe! + call win_gotoid(dum1_id) + bwipe! +endfunc + diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim new file mode 100644 index 0000000000..c4aa6f9218 --- /dev/null +++ b/src/nvim/testdir/test_gf.vim @@ -0,0 +1,33 @@ + +" This is a test if a URL is recognized by "gf", with the cursor before and +" after the "://". Also test ":\\". +function! Test_gf_url() + enew! + call append(0, [ + \ "first test for URL://machine.name/tmp/vimtest2a and other text", + \ "second test for URL://machine.name/tmp/vimtest2b. And other text", + \ "third test for URL:\\\\machine.name\\vimtest2c and other text", + \ "fourth test for URL:\\\\machine.name\\tmp\\vimtest2d, and other text" + \ ]) + call cursor(1,1) + call search("^first") + call search("tmp") + call assert_equal("URL://machine.name/tmp/vimtest2a", expand("<cfile>")) + call search("^second") + call search("URL") + call assert_equal("URL://machine.name/tmp/vimtest2b", expand("<cfile>")) + if has("ebcdic") + set isf=@,240-249,/,.,-,_,+,,,$,:,~,\ + else + set isf=@,48-57,/,.,-,_,+,,,$,:,~,\ + endif + call search("^third") + call search("name") + call assert_equal("URL:\\\\machine.name\\vimtest2c", expand("<cfile>")) + call search("^fourth") + call search("URL") + call assert_equal("URL:\\\\machine.name\\tmp\\vimtest2d", expand("<cfile>")) + + set isf&vim + enew! +endfunction diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim new file mode 100644 index 0000000000..26edc16107 --- /dev/null +++ b/src/nvim/testdir/test_help.vim @@ -0,0 +1,16 @@ + +" Tests for :help + +func Test_help_restore_snapshot() + help + set buftype= + help + edit x + help + helpclose +endfunc + +func Test_help_errors() + call assert_fails('help doesnotexist', 'E149:') + call assert_fails('help!', 'E478:') +endfunc diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim index ee6acfffc3..3163b344d3 100644 --- a/src/nvim/testdir/test_history.vim +++ b/src/nvim/testdir/test_history.vim @@ -63,3 +63,20 @@ function Test_History() call assert_equal(-1, histnr('abc')) call assert_fails('call histnr([])', 'E730:') endfunction + +function Test_Search_history_window() + new + call setline(1, ['a', 'b', 'a', 'b']) + 1 + call feedkeys("/a\<CR>", 'xt') + call assert_equal('a', getline('.')) + 1 + call feedkeys("/b\<CR>", 'xt') + call assert_equal('b', getline('.')) + 1 + " select the previous /a command + call feedkeys("q/kk\<CR>", 'x!') + call assert_equal('a', getline('.')) + call assert_equal('a', @/) + bwipe! +endfunc diff --git a/src/nvim/testdir/test_hlsearch.vim b/src/nvim/testdir/test_hlsearch.vim new file mode 100644 index 0000000000..066fdd0250 --- /dev/null +++ b/src/nvim/testdir/test_hlsearch.vim @@ -0,0 +1,34 @@ +" Test for v:hlsearch + +function! Test_hlsearch() + new + call setline(1, repeat(['aaa'], 10)) + set hlsearch nolazyredraw + let r=[] + " redraw is needed to make hlsearch highlight the matches + exe "normal! /aaa\<CR>" | redraw + let r1 = screenattr(1, 1) + nohlsearch | redraw + call assert_notequal(r1, screenattr(1,1)) + let v:hlsearch=1 | redraw + call assert_equal(r1, screenattr(1,1)) + let v:hlsearch=0 | redraw + call assert_notequal(r1, screenattr(1,1)) + set hlsearch | redraw + call assert_equal(r1, screenattr(1,1)) + let v:hlsearch=0 | redraw + call assert_notequal(r1, screenattr(1,1)) + exe "normal! n" | redraw + call assert_equal(r1, screenattr(1,1)) + let v:hlsearch=0 | redraw + call assert_notequal(r1, screenattr(1,1)) + exe "normal! /\<CR>" | redraw + call assert_equal(r1, screenattr(1,1)) + set nohls + exe "normal! /\<CR>" | redraw + call assert_notequal(r1, screenattr(1,1)) + call assert_fails('let v:hlsearch=[]', 'E745') + call garbagecollect(1) + call getchar(1) + enew! +endfunction diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index d937565ce5..6b313ff54f 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -35,29 +35,81 @@ func Test_map_ctrl_c_visual() endfunc func Test_map_langmap() - " langmap should not get remapped in insert mode - inoremap { FAIL_ilangmap - set langmap=+{ langnoremap + if !has('langmap') + return + endif + + " check langmap applies in normal mode + set langmap=+- nolangremap + new + call setline(1, ['a', 'b', 'c']) + 2 + call assert_equal('b', getline('.')) + call feedkeys("+", "xt") + call assert_equal('a', getline('.')) + + " check no remapping + map x + + 2 + call feedkeys("x", "xt") + call assert_equal('c', getline('.')) + + " check with remapping + set langremap + 2 + call feedkeys("x", "xt") + call assert_equal('a', getline('.')) + + unmap x + bwipe! + + " 'langnoremap' follows 'langremap' and vise versa + set langremap + set langnoremap + call assert_equal(0, &langremap) + set langremap + call assert_equal(0, &langnoremap) + set nolangremap + call assert_equal(1, &langnoremap) + + " check default values + set langnoremap& + call assert_equal(1, &langnoremap) + call assert_equal(0, &langremap) + set langremap& + call assert_equal(1, &langnoremap) + call assert_equal(0, &langremap) + + " langmap should not apply in insert mode, 'langremap' doesn't matter + set langmap=+{ nolangremap call feedkeys("Go+\<Esc>", "xt") call assert_equal('+', getline('$')) - - " Insert-mode expr mapping with langmap - inoremap <expr> { "FAIL_iexplangmap" + set langmap=+{ langremap call feedkeys("Go+\<Esc>", "xt") call assert_equal('+', getline('$')) - iunmap <expr> { - " langmap should not get remapped in Command-line mode - cnoremap { FAIL_clangmap + " langmap used for register name in insert mode. + call setreg('a', 'aaaa') + call setreg('b', 'bbbb') + call setreg('c', 'cccc') + set langmap=ab langremap + call feedkeys("Go\<C-R>a\<Esc>", "xt") + call assert_equal('bbbb', getline('$')) + call feedkeys("Go\<C-R>\<C-R>a\<Esc>", "xt") + call assert_equal('bbbb', getline('$')) + " mapping does not apply + imap c a + call feedkeys("Go\<C-R>c\<Esc>", "xt") + call assert_equal('cccc', getline('$')) + imap a c + call feedkeys("Go\<C-R>a\<Esc>", "xt") + call assert_equal('bbbb', getline('$')) + + " langmap should not apply in Command-line mode + set langmap=+{ nolangremap call feedkeys(":call append(line('$'), '+')\<CR>", "xt") call assert_equal('+', getline('$')) - cunmap { - " Command-line mode expr mapping with langmap - cnoremap <expr> { "FAIL_cexplangmap" - call feedkeys(":call append(line('$'), '+')\<CR>", "xt") - call assert_equal('+', getline('$')) - cunmap { set nomodified endfunc diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim index 9398ef2f27..066bb2f6a1 100644 --- a/src/nvim/testdir/test_match.vim +++ b/src/nvim/testdir/test_match.vim @@ -198,6 +198,16 @@ func Test_matchaddpos() call assert_equal(screenattr(2,2), screenattr(1,10)) call assert_notequal(screenattr(2,2), screenattr(1,11)) + " Check overlapping pos + call clearmatches() + call setline(1, ['1234567890', 'NH']) + call matchaddpos('Error', [[1,1,5], [1,3,5], [2,2]]) + redraw! + call assert_notequal(screenattr(2,2), 0) + call assert_equal(screenattr(2,2), screenattr(1,5)) + call assert_equal(screenattr(2,2), screenattr(1,7)) + call assert_notequal(screenattr(2,2), screenattr(1,8)) + nohl call clearmatches() syntax off diff --git a/src/nvim/testdir/test_matchadd_conceal.vim b/src/nvim/testdir/test_matchadd_conceal.vim index bc1c28d6e9..c788689e33 100644 --- a/src/nvim/testdir/test_matchadd_conceal.vim +++ b/src/nvim/testdir/test_matchadd_conceal.vim @@ -260,3 +260,25 @@ function! Test_matchadd_repeat_conceal_with_syntax_off() quit! endfunction + +function! Test_matchadd_and_syn_conceal() + new + let cnt='Inductive bool : Type := | true : bool | false : bool.' + let expect = 'Inductive - : Type := | true : - | false : -.' + 0put =cnt + " set filetype and :syntax on to change screenattr() + set cole=1 cocu=nv + hi link CheckedByCoq WarningMsg + syntax on + syntax keyword coqKwd bool conceal cchar=- + redraw! + call assert_equal(expect, s:screenline(1)) + call assert_notequal(screenattr(1, 10) , screenattr(1, 11)) + call assert_notequal(screenattr(1, 11) , screenattr(1, 12)) + call assert_equal(screenattr(1, 11) , screenattr(1, 32)) + call matchadd('CheckedByCoq', '\%<2l\%>9c\%<16c') + call assert_equal(expect, s:screenline(1)) + call assert_notequal(screenattr(1, 10) , screenattr(1, 11)) + call assert_notequal(screenattr(1, 11) , screenattr(1, 12)) + call assert_equal(screenattr(1, 11) , screenattr(1, 32)) +endfunction diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 29a7c1edd8..a22dca35cc 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -433,7 +433,6 @@ func! Test_normal13_help() endfunc func! Test_normal14_page() - throw "skipped: Nvim regression: CTRL-F with 'scrolloff'" " basic test for Ctrl-F and Ctrl-B call Setup_NewWindow() exe "norm! \<c-f>" diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 640918b343..75ab01f013 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -599,6 +599,22 @@ function Test_locationlist_curwin_was_closed() augroup! testgroup endfunction +function Test_locationlist_cross_tab_jump() + call writefile(['loclistfoo'], 'loclistfoo') + call writefile(['loclistbar'], 'loclistbar') + set switchbuf=usetab + + edit loclistfoo + tabedit loclistbar + silent lgrep loclistfoo loclist* + call assert_equal(1, tabpagenr()) + + enew | only | tabonly + set switchbuf&vim + call delete('loclistfoo') + call delete('loclistbar') +endfunction + " More tests for 'errorformat' function! Test_efm1() if !has('unix') diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index 9e9a3de500..7f3b31575d 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -97,3 +97,12 @@ func Test_recursive_substitute() call setwinvar(1, 'myvar', 1) bwipe! endfunc + +func Test_eow_with_optional() + let expected = ['abc def', 'abc', 'def', '', '', '', '', '', '', ''] + for re in range(0, 2) + exe 'set re=' . re + let actual = matchlist('abc def', '\(abc\>\)\?\s*\(def\)') + call assert_equal(expected, actual) + endfor +endfunc diff --git a/src/nvim/testdir/test_smartindent.vim b/src/nvim/testdir/test_smartindent.vim new file mode 100644 index 0000000000..d00eac9798 --- /dev/null +++ b/src/nvim/testdir/test_smartindent.vim @@ -0,0 +1,14 @@ + +" Tests for not doing smart indenting when it isn't set. +function! Test_nosmartindent() + new + call append(0, [" some test text", + \ " test text", + \ "test text", + \ " test text"]) + set nocindent nosmartindent autoindent + exe "normal! gg/some\<CR>" + exe "normal! 2cc#test\<Esc>" + call assert_equal(" #test", getline(1)) + enew! | close +endfunction diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim new file mode 100644 index 0000000000..f78d92f3ff --- /dev/null +++ b/src/nvim/testdir/test_startup.vim @@ -0,0 +1,200 @@ +" Tests for startup. + +source shared.vim + +" Check that loading startup.vim works. +func Test_startup_script() + throw 'skipped: Nvim does not need defaults.vim' + set compatible + source $VIMRUNTIME/defaults.vim + + call assert_equal(0, &compatible) +endfunc + +" Verify the order in which plugins are loaded: +" 1. plugins in non-after directories +" 2. packages +" 3. plugins in after directories +func Test_after_comes_later() + if !has('packages') + return + endif + let before = [ + \ 'set nocp viminfo+=nviminfo', + \ 'set guioptions+=M', + \ 'let $HOME = "/does/not/exist"', + \ 'set loadplugins', + \ 'set rtp=Xhere,Xafter', + \ 'set packpath=Xhere,Xafter', + \ 'set nomore', + \ ] + let after = [ + \ 'redir! > Xtestout', + \ 'scriptnames', + \ 'redir END', + \ 'quit', + \ ] + call mkdir('Xhere/plugin', 'p') + call writefile(['let done = 1'], 'Xhere/plugin/here.vim') + call mkdir('Xhere/pack/foo/start/foobar/plugin', 'p') + call writefile(['let done = 1'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim') + + call mkdir('Xafter/plugin', 'p') + call writefile(['let done = 1'], 'Xafter/plugin/later.vim') + + if RunVim(before, after, '') + + let lines = readfile('Xtestout') + let expected = ['Xbefore.vim', 'here.vim', 'foo.vim', 'later.vim', 'Xafter.vim'] + let found = [] + for line in lines + for one in expected + if line =~ one + call add(found, one) + endif + endfor + endfor + call assert_equal(expected, found) + endif + + call delete('Xtestout') + call delete('Xhere', 'rf') + call delete('Xafter', 'rf') +endfunc + +func Test_help_arg() + if !has('unix') && has('gui') + " this doesn't work with gvim on MS-Windows + return + endif + if RunVim([], [], '--help >Xtestout') + let lines = readfile('Xtestout') + call assert_true(len(lines) > 20) + call assert_match('Usage:', lines[0]) + + " check if couple of lines are there + let found = [] + for line in lines + if line =~ '-R.*Readonly mode' + call add(found, 'Readonly mode') + endif + " Watch out for a second --version line in the Gnome version. + if line =~ '--version.*Print version information and exit' + call add(found, "--version") + endif + endfor + call assert_equal(['--version'], found) + endif + call delete('Xtestout') +endfunc + +func Test_compatible_args() + throw "skipped: Nvim is always 'nocompatible'" + let after = [ + \ 'call writefile([string(&compatible)], "Xtestout")', + \ 'set viminfo+=nviminfo', + \ 'quit', + \ ] + if RunVim([], after, '-C') + let lines = readfile('Xtestout') + call assert_equal('1', lines[0]) + endif + + if RunVim([], after, '-N') + let lines = readfile('Xtestout') + call assert_equal('0', lines[0]) + endif + + call delete('Xtestout') +endfunc + +func Test_file_args() + let after = [ + \ 'call writefile(argv(), "Xtestout")', + \ 'qall', + \ ] + if RunVim([], after, '') + let lines = readfile('Xtestout') + call assert_equal(0, len(lines)) + endif + + if RunVim([], after, 'one') + let lines = readfile('Xtestout') + call assert_equal(1, len(lines)) + call assert_equal('one', lines[0]) + endif + + if RunVim([], after, 'one two three') + let lines = readfile('Xtestout') + call assert_equal(3, len(lines)) + call assert_equal('one', lines[0]) + call assert_equal('two', lines[1]) + call assert_equal('three', lines[2]) + endif + + if RunVim([], after, 'one -c echo two') + let lines = readfile('Xtestout') + call assert_equal(2, len(lines)) + call assert_equal('one', lines[0]) + call assert_equal('two', lines[1]) + endif + + if RunVim([], after, 'one -- -c echo two') + let lines = readfile('Xtestout') + call assert_equal(4, len(lines)) + call assert_equal('one', lines[0]) + call assert_equal('-c', lines[1]) + call assert_equal('echo', lines[2]) + call assert_equal('two', lines[3]) + endif + + call delete('Xtestout') +endfunc + +func Test_startuptime() + if !has('startuptime') + return + endif + let after = ['qall'] + if RunVim([], after, '--startuptime Xtestout one') + let lines = readfile('Xtestout') + let expected = ['parsing arguments', 'inits 3', 'opening buffers'] + let found = [] + for line in lines + for exp in expected + if line =~ exp + call add(found, exp) + endif + endfor + endfor + call assert_equal(expected, found) + endif + call delete('Xtestout') +endfunc + +func Test_read_stdin() + let after = [ + \ 'write Xtestout', + \ 'quit!', + \ ] + if RunVimPiped([], after, '-', 'echo something | ') + let lines = readfile('Xtestout') + " MS-Windows adds a space after the word + call assert_equal(['something'], split(lines[0])) + endif + call delete('Xtestout') +endfunc + +func Test_progpath() + " Tests normally run with "./vim" or "../vim", these must have been expanded + " to a full path. + if has('unix') + call assert_equal('/', v:progpath[0]) + elseif has('win32') + call assert_equal(':', v:progpath[1]) + call assert_match('[/\\]', v:progpath[2]) + endif + + " Only expect "vim" to appear in v:progname. + call assert_match('vim\c', v:progname) +endfunc diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim new file mode 100644 index 0000000000..e2b6de03c3 --- /dev/null +++ b/src/nvim/testdir/test_substitute.vim @@ -0,0 +1,41 @@ +" Tests for multi-line regexps with ":s". + +function! Test_multiline_subst() + enew! + call append(0, ["1 aa", + \ "bb", + \ "cc", + \ "2 dd", + \ "ee", + \ "3 ef", + \ "gh", + \ "4 ij", + \ "5 a8", + \ "8b c9", + \ "9d", + \ "6 e7", + \ "77f", + \ "xxxxx"]) + + 1 + " test if replacing a line break works with a back reference + /^1/,/^2/s/\n\(.\)/ \1/ + " test if inserting a line break works with a back reference + /^3/,/^4/s/\(.\)$/\r\1/ + " test if replacing a line break with another line break works + /^5/,/^6/s/\(\_d\{3}\)/x\1x/ + call assert_equal('1 aa bb cc 2 dd ee', getline(1)) + call assert_equal('3 e', getline(2)) + call assert_equal('f', getline(3)) + call assert_equal('g', getline(4)) + call assert_equal('h', getline(5)) + call assert_equal('4 i', getline(6)) + call assert_equal('j', getline(7)) + call assert_equal('5 ax8', getline(8)) + call assert_equal('8xb cx9', getline(9)) + call assert_equal('9xd', getline(10)) + call assert_equal('6 ex7', getline(11)) + call assert_equal('7x7f', getline(12)) + call assert_equal('xxxxx', getline(13)) + enew! +endfunction diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 7c3039ba24..33139fcda0 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -11,6 +11,7 @@ function Test_tabpage() 0tabnew 1tabnew $tabnew + %del tabdo call append(line('$'), tabpagenr()) tabclose! 2 tabrewind @@ -93,10 +94,6 @@ function Test_tabpage() call assert_equal(7, tabpagenr()) tabmove call assert_equal(10, tabpagenr()) - tabmove -20 - call assert_equal(1, tabpagenr()) - tabmove +20 - call assert_equal(10, tabpagenr()) 0tabmove call assert_equal(1, tabpagenr()) $tabmove @@ -109,7 +106,16 @@ function Test_tabpage() call assert_equal(4, tabpagenr()) 7tabmove 5 call assert_equal(5, tabpagenr()) + call assert_fails("99tabmove", 'E16:') + call assert_fails("+99tabmove", 'E16:') + call assert_fails("-99tabmove", 'E16:') call assert_fails("tabmove foo", 'E474:') + call assert_fails("tabmove 99", 'E474:') + call assert_fails("tabmove +99", 'E474:') + call assert_fails("tabmove -99", 'E474:') + call assert_fails("tabmove -3+", 'E474:') + call assert_fails("tabmove $3", 'E474:') + 1tabonly! endfunc " Test autocommands @@ -117,7 +123,6 @@ function Test_tabpage_with_autocmd() if !has('autocmd') return endif - tabonly! command -nargs=1 -bar C :call add(s:li, '=== ' . <q-args> . ' ===')|<args> augroup TestTabpageGroup au! @@ -182,8 +187,10 @@ function Test_tabpage_with_autocmd() autocmd TabDestructive TabEnter * nested :C tabnext 2 | C tabclose 3 let s:li = [] - C tabnext 3 - call assert_equal(['=== tabnext 3 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', '=== tabclose 3 ===', 'BufEnter', '=== tabclose 3 ==='], s:li) + call assert_equal(3, tabpagenr('$')) + C tabnext 2 + call assert_equal(2, tabpagenr('$')) + call assert_equal(['=== tabnext 2 ===', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', '=== tabclose 3 ==='], s:li) call assert_equal(['2/2'], [tabpagenr().'/'.tabpagenr('$')]) delcommand C @@ -191,8 +198,7 @@ function Test_tabpage_with_autocmd() augroup! TabDestructive autocmd! TestTabpageGroup augroup! TestTabpageGroup - tabonly! - bw! + 1tabonly! endfunction function Test_tabpage_with_tab_modifier() @@ -204,7 +210,7 @@ function Test_tabpage_with_tab_modifier() exec 'tabnext ' . a:pre_nr exec a:cmd call assert_equal(a:post_nr, tabpagenr()) - call assert_equal('help', &filetype) + call assert_equal('help', &buftype) helpclose endfunc @@ -223,8 +229,223 @@ function Test_tabpage_with_tab_modifier() call assert_fails('-99tab help', 'E16:') delfunction s:check_tab - tabonly! - bw! + 1tabonly! +endfunction + +function Check_tab_count(pre_nr, cmd, post_nr) + exec 'tabnext' a:pre_nr + normal! G + exec a:cmd + call assert_equal(a:post_nr, tabpagenr(), a:cmd) +endfunc + +" Test for [count] of tabnext +function Test_tabpage_with_tabnext() + for n in range(4) + tabedit + call setline(1, ['', '', '3']) + endfor + + call Check_tab_count(1, 'tabnext', 2) + call Check_tab_count(1, '3tabnext', 3) + call Check_tab_count(1, '.tabnext', 1) + call Check_tab_count(1, '.+1tabnext', 2) + call Check_tab_count(2, '+tabnext', 3) + call Check_tab_count(2, '+2tabnext', 4) + call Check_tab_count(4, '-tabnext', 3) + call Check_tab_count(4, '-2tabnext', 2) + call Check_tab_count(3, '$tabnext', 5) + call assert_fails('0tabnext', 'E16:') + call assert_fails('99tabnext', 'E16:') + call assert_fails('+99tabnext', 'E16:') + call assert_fails('-99tabnext', 'E16:') + call Check_tab_count(1, 'tabnext 3', 3) + call Check_tab_count(2, 'tabnext +', 3) + call Check_tab_count(2, 'tabnext +2', 4) + call Check_tab_count(4, 'tabnext -', 3) + call Check_tab_count(4, 'tabnext -2', 2) + call Check_tab_count(3, 'tabnext $', 5) + call assert_fails('tabnext 0', 'E474:') + call assert_fails('tabnext .', 'E474:') + call assert_fails('tabnext -+', 'E474:') + call assert_fails('tabnext +2-', 'E474:') + call assert_fails('tabnext $3', 'E474:') + call assert_fails('tabnext 99', 'E474:') + call assert_fails('tabnext +99', 'E474:') + call assert_fails('tabnext -99', 'E474:') + + 1tabonly! +endfunction + +" Test for [count] of tabprevious +function Test_tabpage_with_tabprevious() + for n in range(5) + tabedit + call setline(1, ['', '', '3']) + endfor + + for cmd in ['tabNext', 'tabprevious'] + call Check_tab_count(6, cmd, 5) + call Check_tab_count(6, '3' . cmd, 3) + call Check_tab_count(6, '8' . cmd, 4) + call Check_tab_count(6, cmd . ' 3', 3) + call Check_tab_count(6, cmd . ' 8', 4) + for n in range(2) + for c in ['0', '.+3', '+', '+2' , '-', '-2' , '$', '+99', '-99'] + if n == 0 " pre count + let entire_cmd = c . cmd + let err_code = 'E16:' + else + let entire_cmd = cmd . ' ' . c + let err_code = 'E474:' + endif + call assert_fails(entire_cmd, err_code) + endfor + endfor + endfor + + 1tabonly! +endfunction + +function s:reconstruct_tabpage_for_test(nr) + let n = (a:nr > 2) ? a:nr - 2 : 1 + 1tabonly! + 0tabedit n0 + for n in range(1, n) + exec '$tabedit n' . n + if n == 1 + call setline(1, ['', '', '3']) + endif + endfor +endfunc + +" Test for [count] of tabclose +function Test_tabpage_with_tabclose() + + " pre count + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(3, 'tabclose!', 3) + call Check_tab_count(1, '3tabclose', 1) + call Check_tab_count(4, '4tabclose', 3) + call Check_tab_count(3, '1tabclose', 2) + call Check_tab_count(2, 'tabclose', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(2, '$tabclose', 2) + call Check_tab_count(4, '.tabclose', 4) + call Check_tab_count(3, '.+tabclose', 3) + call Check_tab_count(3, '.-2tabclose', 2) + call Check_tab_count(1, '.+1tabclose!', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + " post count + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(3, 'tabclose!', 3) + call Check_tab_count(1, 'tabclose 3', 1) + call Check_tab_count(4, 'tabclose 4', 3) + call Check_tab_count(3, 'tabclose 1', 2) + call Check_tab_count(2, 'tabclose', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(2, 'tabclose $', 2) + call Check_tab_count(4, 'tabclose', 4) + call Check_tab_count(3, 'tabclose +', 3) + call Check_tab_count(3, 'tabclose -2', 2) + call Check_tab_count(1, 'tabclose! +1', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + call s:reconstruct_tabpage_for_test(6) + for n in range(2) + for c in ['0', '$3', '99', '+99', '-99'] + if n == 0 " pre count + let entire_cmd = c . 'tabclose' + let err_code = 'E16:' + else + let entire_cmd = 'tabclose ' . c + let err_code = 'E474:' + endif + call assert_fails(entire_cmd, err_code) + call assert_equal(6, tabpagenr('$')) + endfor + endfor + + call assert_fails('3tabclose', 'E37:') + call assert_fails('tabclose 3', 'E37:') + call assert_fails('tabclose -+', 'E474:') + call assert_fails('tabclose +2-', 'E474:') + call assert_equal(6, tabpagenr('$')) + + 1tabonly! +endfunction + +" Test for [count] of tabonly +function Test_tabpage_with_tabonly() + + " Test for the normal behavior (pre count only) + let tc = [ [4, '.', '!'], [2, '.+', ''], [3, '.-2', '!'], [1, '.+1', '!'] ] + for c in tc + call s:reconstruct_tabpage_for_test(6) + let entire_cmd = c[1] . 'tabonly' . c[2] + call Check_tab_count(c[0], entire_cmd, 1) + call assert_equal(1, tabpagenr('$')) + endfor + + " Test for the normal behavior + let tc2 = [ [3, '', ''], [1, '3', ''], [4, '4', '!'], [3, '1', '!'], + \ [2, '', '!'], + \ [2, '$', '!'], [3, '+', '!'], [3, '-2', '!'], [3, '+1', '!'] + \ ] + for n in range(2) + for c in tc2 + call s:reconstruct_tabpage_for_test(6) + if n == 0 " pre count + let entire_cmd = c[1] . 'tabonly' . c[2] + else + let entire_cmd = 'tabonly' . c[2] . ' ' . c[1] + endif + call Check_tab_count(c[0], entire_cmd, 1) + call assert_equal(1, tabpagenr('$')) + endfor + endfor + + " Test for the error behavior + for n in range(2) + for c in ['0', '$3', '99', '+99', '-99'] + call s:reconstruct_tabpage_for_test(6) + if n == 0 " pre count + let entire_cmd = c . 'tabonly' + let err_code = 'E16:' + else + let entire_cmd = 'tabonly ' . c + let err_code = 'E474:' + endif + call assert_fails(entire_cmd, err_code) + call assert_equal(6, tabpagenr('$')) + endfor + endfor + + " Test for the error behavior (post count only) + for c in tc + call s:reconstruct_tabpage_for_test(6) + let entire_cmd = 'tabonly' . c[2] . ' ' . c[1] + let err_code = 'E474:' + call assert_fails(entire_cmd, err_code) + call assert_equal(6, tabpagenr('$')) + endfor + + call assert_fails('tabonly -+', 'E474:') + call assert_fails('tabonly +2-', 'E474:') + call assert_equal(6, tabpagenr('$')) + + 1tabonly! + new + only! endfunction func Test_tabnext_on_buf_unload1() diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 2044c23f79..0d697b3f3e 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -65,4 +65,34 @@ func Test_duplicate_tagjump() call delete('Xfile1') endfunc +" Tests for [ CTRL-I and CTRL-W CTRL-I commands +function Test_keyword_jump() + call writefile(["#include Xinclude", "", + \ "", + \ "/* test text test tex start here", + \ " some text", + \ " test text", + \ " start OK if found this line", + \ " start found wrong line", + \ "test text"], 'Xtestfile') + call writefile(["/* test text test tex start here", + \ " some text", + \ " test text", + \ " start OK if found this line", + \ " start found wrong line", + \ "test text"], 'Xinclude') + new Xtestfile + call cursor(1,1) + call search("start") + exe "normal! 5[\<C-I>" + call assert_equal(" start OK if found this line", getline('.')) + call cursor(1,1) + call search("start") + exe "normal! 5\<C-W>\<C-I>" + call assert_equal(" start OK if found this line", getline('.')) + enew! | only + call delete('Xtestfile') + call delete('Xinclude') +endfunction + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index fc61d1f223..9ff73fd870 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -131,6 +131,39 @@ func Test_undo_del_chars() close! endfunc +func Test_undolist() + new + set ul=100 + + let a=execute('undolist') + call assert_equal("\nNothing to undo", a) + + " 1 leaf (2 changes). + call feedkeys('achange1', 'xt') + call feedkeys('achange2', 'xt') + let a=execute('undolist') + call assert_match("^\nnumber changes when *saved\n *2 *2 .*$", a) + + " 2 leaves. + call feedkeys('u', 'xt') + call feedkeys('achange3\<Esc>', 'xt') + let a=execute('undolist') + call assert_match("^\nnumber changes when *saved\n *2 *2 *.*\n *3 *2 .*$", a) + close! +endfunc + +func Test_U_command() + new + set ul=100 + call feedkeys("achange1\<Esc>", 'xt') + call feedkeys("achange2\<Esc>", 'xt') + norm! U + call assert_equal('', getline(1)) + norm! U + call assert_equal('change1change2', getline(1)) + close! +endfunc + func Test_undojoin() new call feedkeys("Goaaaa\<Esc>", 'xt') diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim new file mode 100644 index 0000000000..24e3db86fb --- /dev/null +++ b/src/nvim/testdir/test_utf8.vim @@ -0,0 +1,65 @@ +" Tests for Unicode manipulations +if !has('multi_byte') + finish +endif + + +" Visual block Insert adjusts for multi-byte char +func Test_visual_block_insert() + new + call setline(1, ["aaa", "あああ", "bbb"]) + exe ":norm! gg0l\<C-V>jjIx\<Esc>" + call assert_equal(['axaa', 'xあああ', 'bxbb'], getline(1, '$')) + bwipeout! +endfunc + +" Test for built-in function strchars() +func Test_strchars() + let inp = ["a", "あいa", "A\u20dd", "A\u20dd\u20dd", "\u20dd"] + let exp = [[1, 1, 1], [3, 3, 3], [2, 2, 1], [3, 3, 1], [1, 1, 1]] + for i in range(len(inp)) + call assert_equal(exp[i][0], strchars(inp[i])) + call assert_equal(exp[i][1], strchars(inp[i], 0)) + call assert_equal(exp[i][2], strchars(inp[i], 1)) + endfor +endfunc + +" Test for customlist completion +function! CustomComplete1(lead, line, pos) + return ['あ', 'い'] +endfunction + +function! CustomComplete2(lead, line, pos) + return ['あたし', 'あたま', 'あたりめ'] +endfunction + +function! CustomComplete3(lead, line, pos) + return ['Nこ', 'Nん', 'Nぶ'] +endfunction + +func Test_customlist_completion() + command -nargs=1 -complete=customlist,CustomComplete1 Test1 echo + call feedkeys(":Test1 \<C-L>\<C-B>\"\<CR>", 'itx') + call assert_equal('"Test1 ', getreg(':')) + + command -nargs=1 -complete=customlist,CustomComplete2 Test2 echo + call feedkeys(":Test2 \<C-L>\<C-B>\"\<CR>", 'itx') + call assert_equal('"Test2 あた', getreg(':')) + + command -nargs=1 -complete=customlist,CustomComplete3 Test3 echo + call feedkeys(":Test3 \<C-L>\<C-B>\"\<CR>", 'itx') + call assert_equal('"Test3 N', getreg(':')) + + call garbagecollect(1) +endfunc + +" Yank one 3 byte character and check the mark columns. +func Test_getvcol() + new + call setline(1, "x\u2500x") + normal 0lvy + call assert_equal(2, col("'[")) + call assert_equal(4, col("']")) + call assert_equal(2, virtcol("'[")) + call assert_equal(2, virtcol("']")) +endfunc diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 569a78a0ed..188a7ed0f3 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -67,4 +67,316 @@ function Test_window_cmd_wincmd_gf() augroup! test_window_cmd_wincmd_gf endfunc +func Test_next_split_all() + " This was causing an illegal memory access. + n x + norm axxx + split + split + s/x + s/x + all + bwipe! +endfunc + +func Test_window_quit() + e Xa + split Xb + call assert_equal(2, winnr('$')) + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + + wincmd q + call assert_equal(1, winnr('$')) + call assert_equal('Xa', bufname(winbufnr(1))) + + bw Xa Xb +endfunc + +func Test_window_horizontal_split() + call assert_equal(1, winnr('$')) + 3wincmd s + call assert_equal(2, winnr('$')) + call assert_equal(3, winheight(0)) + call assert_equal(winwidth(1), winwidth(2)) + + call assert_fails('botright topleft wincmd s', 'E442:') + bw +endfunc + +func Test_window_vertical_split() + call assert_equal(1, winnr('$')) + 3wincmd v + call assert_equal(2, winnr('$')) + call assert_equal(3, winwidth(0)) + call assert_equal(winheight(1), winheight(2)) + + call assert_fails('botright topleft wincmd v', 'E442:') + bw +endfunc + +func Test_window_split_edit_alternate() + e Xa + e Xb + + wincmd ^ + call assert_equal('Xa', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + + bw Xa Xb +endfunc + +func Test_window_preview() + " Open a preview window + pedit Xa + call assert_equal(2, winnr('$')) + call assert_equal(0, &previewwindow) + + " Go to the preview window + wincmd P + call assert_equal(1, &previewwindow) + + " Close preview window + wincmd z + call assert_equal(1, winnr('$')) + call assert_equal(0, &previewwindow) + + call assert_fails('wincmd P', 'E441:') +endfunc + +func Test_window_exchange() + e Xa + + " Nothing happens with window exchange when there is 1 window + wincmd x + call assert_equal(1, winnr('$')) + + split Xb + split Xc + + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Exchange current window 1 with window 3 + 3wincmd x + call assert_equal('Xa', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + " Exchange window with next when at the top window + wincmd x + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + " Exchange window with next when at the middle window + wincmd j + wincmd x + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xc', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Exchange window with next when at the bottom window. + " When there is no next window, it exchanges with the previous window. + wincmd j + wincmd x + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + bw Xa Xb Xc +endfunc + +func Test_window_rotate() + e Xa + split Xb + split Xc + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Rotate downwards + wincmd r + call assert_equal('Xa', bufname(winbufnr(1))) + call assert_equal('Xc', bufname(winbufnr(2))) + call assert_equal('Xb', bufname(winbufnr(3))) + + 2wincmd r + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Rotate upwards + wincmd R + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + 2wincmd R + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + bot vsplit + call assert_fails('wincmd R', 'E443:') + + bw Xa Xb Xc +endfunc + +func Test_window_height() + e Xa + split Xb + + let [wh1, wh2] = [winheight(1), winheight(2)] + " Active window (1) should have the same height or 1 more + " than the other window. + call assert_inrange(wh2, wh2 + 1, wh1) + + wincmd - + call assert_equal(wh1 - 1, winheight(1)) + call assert_equal(wh2 + 1, winheight(2)) + + wincmd + + call assert_equal(wh1, winheight(1)) + call assert_equal(wh2, winheight(2)) + + 2wincmd _ + call assert_equal(2, winheight(1)) + call assert_equal(wh1 + wh2 - 2, winheight(2)) + + wincmd = + call assert_equal(wh1, winheight(1)) + call assert_equal(wh2, winheight(2)) + + 2wincmd _ + set winfixheight + split Xc + let [wh1, wh2, wh3] = [winheight(1), winheight(2), winheight(3)] + call assert_equal(2, winheight(2)) + call assert_inrange(wh3, wh3 + 1, wh1) + 3wincmd + + call assert_equal(2, winheight(2)) + call assert_equal(wh1 + 3, winheight(1)) + call assert_equal(wh3 - 3, winheight(3)) + wincmd = + call assert_equal(2, winheight(2)) + call assert_equal(wh1, winheight(1)) + call assert_equal(wh3, winheight(3)) + + wincmd j + set winfixheight& + + wincmd = + let [wh1, wh2, wh3] = [winheight(1), winheight(2), winheight(3)] + " Current window (2) should have the same height or 1 more + " than the other windows. + call assert_inrange(wh1, wh1 + 1, wh2) + call assert_inrange(wh3, wh3 + 1, wh2) + + bw Xa Xb Xc +endfunc + +func Test_window_width() + e Xa + vsplit Xb + + let [ww1, ww2] = [winwidth(1), winwidth(2)] + " Active window (1) should have the same width or 1 more + " than the other window. + call assert_inrange(ww2, ww2 + 1, ww1) + + wincmd < + call assert_equal(ww1 - 1, winwidth(1)) + call assert_equal(ww2 + 1, winwidth(2)) + + wincmd > + call assert_equal(ww1, winwidth(1)) + call assert_equal(ww2, winwidth(2)) + + 2wincmd | + call assert_equal(2, winwidth(1)) + call assert_equal(ww1 + ww2 - 2, winwidth(2)) + + wincmd = + call assert_equal(ww1, winwidth(1)) + call assert_equal(ww2, winwidth(2)) + + 2wincmd | + set winfixwidth + vsplit Xc + let [ww1, ww2, ww3] = [winwidth(1), winwidth(2), winwidth(3)] + " FIXME: commented out: I would expect the width of 2nd window to + " remain 2 but it's actually 1?! + "call assert_equal(2, winwidth(2)) + call assert_inrange(ww3, ww3 + 1, ww1) + 3wincmd > + " FIXME: commented out: I would expect the width of 2nd window to + " remain 2 but it's actually 1?! + "call assert_equal(2, winwidth(2)) + call assert_equal(ww1 + 3, winwidth(1)) + call assert_equal(ww3 - 3, winwidth(3)) + wincmd = + " FIXME: commented out: I would expect the width of 2nd window to + " remain 2 but it's actually 1?! + "call assert_equal(2, winwidth(2)) + call assert_equal(ww1, winwidth(1)) + call assert_equal(ww3, winwidth(3)) + + wincmd l + set winfixwidth& + + wincmd = + let [ww1, ww2, ww3] = [winwidth(1), winwidth(2), winwidth(3)] + " Current window (2) should have the same width or 1 more + " than the other windows. + call assert_inrange(ww1, ww1 + 1, ww2) + call assert_inrange(ww3, ww3 + 1, ww2) + + bw Xa Xb Xc +endfunc + +func Test_window_jump_tag() + help + /iccf + call assert_match('^|iccf|', getline('.')) + call assert_equal(2, winnr('$')) + 2wincmd } + call assert_equal(3, winnr('$')) + call assert_match('^|iccf|', getline('.')) + wincmd k + call assert_match('\*iccf\*', getline('.')) + call assert_equal(2, winheight(0)) + + wincmd z + set previewheight=4 + help + /bugs + wincmd } + wincmd k + call assert_match('\*bugs\*', getline('.')) + call assert_equal(4, winheight(0)) + set previewheight& + + %bw! +endfunc + +func Test_window_newtab() + e Xa + + call assert_equal(1, tabpagenr('$')) + call assert_equal("\nAlready only one window", execute('wincmd T')) + + split Xb + split Xc + + wincmd T + call assert_equal(2, tabpagenr('$')) + call assert_equal(['Xb', 'Xa'], map(tabpagebuflist(1), 'bufname(v:val)')) + call assert_equal(['Xc' ], map(tabpagebuflist(2), 'bufname(v:val)')) + + %bw! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_window_id.vim b/src/nvim/testdir/test_window_id.vim index 66656e1d0a..b3b506d04d 100644 --- a/src/nvim/testdir/test_window_id.vim +++ b/src/nvim/testdir/test_window_id.vim @@ -92,3 +92,12 @@ func Test_win_getid() only! endfunc + +func Test_win_getid_curtab() + tabedit X + tabfirst + copen + only + call assert_equal(win_getid(1), win_getid(1, 1)) + tabclose! +endfunc diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 9fbbe8be92..f34f5f1bc4 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -31,6 +31,8 @@ #include "nvim/ugrid.h" #include "nvim/tui/input.h" #include "nvim/tui/tui.h" +#include "nvim/cursor_shape.h" +#include "nvim/syntax.h" // Space reserved in the output buffer to restore the cursor to normal when // flushing. No existing terminal will require 32 bytes to do that. @@ -69,18 +71,20 @@ typedef struct { bool can_use_terminal_scroll; bool mouse_enabled; bool busy; + cursorentry_T cursor_shapes[SHAPE_IDX_COUNT]; HlAttrs print_attrs; int showing_mode; struct { int enable_mouse, disable_mouse; int enable_bracketed_paste, disable_bracketed_paste; - int set_cursor_shape_bar, set_cursor_shape_ul, set_cursor_shape_block; int set_rgb_foreground, set_rgb_background; + int set_cursor_color; int enable_focus_reporting, disable_focus_reporting; } unibi_ext; } TUIData; static bool volatile got_winch = false; +static bool cursor_style_enabled = false; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.c.generated.h" @@ -97,6 +101,7 @@ UI *tui_start(void) ui->clear = tui_clear; ui->eol_clear = tui_eol_clear; ui->cursor_goto = tui_cursor_goto; + ui->cursor_style_set = tui_cursor_style_set; ui->update_menu = tui_update_menu; ui->busy_start = tui_busy_start; ui->busy_stop = tui_busy_stop; @@ -129,11 +134,9 @@ static void terminfo_start(UI *ui) data->showing_mode = 0; data->unibi_ext.enable_mouse = -1; data->unibi_ext.disable_mouse = -1; + data->unibi_ext.set_cursor_color = -1; data->unibi_ext.enable_bracketed_paste = -1; data->unibi_ext.disable_bracketed_paste = -1; - data->unibi_ext.set_cursor_shape_bar = -1; - data->unibi_ext.set_cursor_shape_ul = -1; - data->unibi_ext.set_cursor_shape_block = -1; data->unibi_ext.enable_focus_reporting = -1; data->unibi_ext.disable_focus_reporting = -1; data->out_fd = 1; @@ -146,11 +149,10 @@ static void terminfo_start(UI *ui) data->ut = unibi_dummy(); } fix_terminfo(data); - // Initialize the cursor shape. - unibi_out(ui, data->unibi_ext.set_cursor_shape_block); // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); // Enter alternate screen and clear + // NOTE: Do this *before* changing terminal settings. #6433 unibi_out(ui, unibi_enter_ca_mode); unibi_out(ui, unibi_clear_screen); // Enable bracketed paste @@ -434,6 +436,65 @@ static void tui_cursor_goto(UI *ui, int row, int col) unibi_goto(ui, row, col); } +CursorShape tui_cursor_decode_shape(const char *shape_str) +{ + CursorShape shape = 0; + if (strequal(shape_str, "block")) { + shape = SHAPE_BLOCK; + } else if (strequal(shape_str, "vertical")) { + shape = SHAPE_VER; + } else if (strequal(shape_str, "horizontal")) { + shape = SHAPE_HOR; + } else { + EMSG2(_(e_invarg2), shape_str); + } + return shape; +} + +static cursorentry_T decode_cursor_entry(Dictionary args) +{ + cursorentry_T r; + + for (size_t i = 0; i < args.size; i++) { + char *key = args.items[i].key.data; + Object value = args.items[i].value; + + if (strequal(key, "cursor_shape")) { + r.shape = tui_cursor_decode_shape(args.items[i].value.data.string.data); + } else if (strequal(key, "blinkon")) { + r.blinkon = (int)value.data.integer; + } else if (strequal(key, "blinkoff")) { + r.blinkoff = (int)value.data.integer; + } else if (strequal(key, "hl_id")) { + r.id = (int)value.data.integer; + } + } + return r; +} + +static void tui_cursor_style_set(UI *ui, bool enabled, Dictionary args) +{ + cursor_style_enabled = enabled; + if (!enabled) { + return; // Do not send cursor style control codes. + } + TUIData *data = ui->data; + + assert(args.size); + // Keys: as defined by `shape_table`. + for (size_t i = 0; i < args.size; i++) { + char *mode_name = args.items[i].key.data; + const int mode_id = cursor_mode_str2int(mode_name); + assert(mode_id >= 0); + cursorentry_T r = decode_cursor_entry(args.items[i].value.data.dictionary); + r.full_name = mode_name; + data->cursor_shapes[mode_id] = r; + } + + MouseMode cursor_mode = tui_mode2cursor(data->showing_mode); + tui_set_cursor(ui, cursor_mode); +} + static void tui_update_menu(UI *ui) { // Do nothing; menus are for GUI only @@ -452,44 +513,117 @@ static void tui_busy_stop(UI *ui) static void tui_mouse_on(UI *ui) { TUIData *data = ui->data; - unibi_out(ui, data->unibi_ext.enable_mouse); - data->mouse_enabled = true; + if (!data->mouse_enabled) { + unibi_out(ui, data->unibi_ext.enable_mouse); + data->mouse_enabled = true; + } } static void tui_mouse_off(UI *ui) { TUIData *data = ui->data; - unibi_out(ui, data->unibi_ext.disable_mouse); - data->mouse_enabled = false; + if (data->mouse_enabled) { + unibi_out(ui, data->unibi_ext.disable_mouse); + data->mouse_enabled = false; + } +} + +/// @param mode one of SHAPE_XXX +static void tui_set_cursor(UI *ui, MouseMode mode) +{ + if (!cursor_style_enabled) { + return; + } + TUIData *data = ui->data; + cursorentry_T c = data->cursor_shapes[mode]; + int shape = c.shape; + bool inside_tmux = os_getenv("TMUX") != NULL; + unibi_var_t vars[26 + 26] = { { 0 } }; + +# define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) + // Support changing cursor shape on some popular terminals. + const char *vte_version = os_getenv("VTE_VERSION"); + + if (os_getenv("KONSOLE_PROFILE_NAME") || os_getenv("KONSOLE_DBUS_SESSION")) { + // Konsole uses a proprietary escape code to set the cursor shape + // and does not support DECSCUSR. + switch (shape) { + case SHAPE_BLOCK: shape = 0; break; + case SHAPE_VER: shape = 1; break; + case SHAPE_HOR: shape = 2; break; + default: WLOG("Unknown shape value %d", shape); break; + } + data->params[0].i = shape; + data->params[1].i = (c.blinkon == 0); + + unibi_format(vars, vars + 26, + TMUX_WRAP("\x1b]50;CursorShape=%p1%d;BlinkingCursorEnabled=%p2%d\x07"), + data->params, out, ui, NULL, NULL); + } else if (!vte_version || atoi(vte_version) >= 3900) { + // Assume that the terminal supports DECSCUSR unless it is an + // old VTE based terminal. This should not get wrapped for tmux, + // which will handle it via its Ss/Se terminfo extension - usually + // according to its terminal-overrides. + + switch (shape) { + case SHAPE_BLOCK: shape = 1; break; + case SHAPE_VER: shape = 5; break; + case SHAPE_HOR: shape = 3; break; + default: WLOG("Unknown shape value %d", shape); break; + } + data->params[0].i = shape + (c.blinkon ==0); + unibi_format(vars, vars + 26, "\x1b[%p1%d q", + data->params, out, ui, NULL, NULL); + } + + if (c.id != 0 && ui->rgb) { + int attr = syn_id2attr(c.id); + attrentry_T *aep = syn_cterm_attr2entry(attr); + data->params[0].i = aep->rgb_bg_color; + unibi_out(ui, data->unibi_ext.set_cursor_color); + } +} + +/// Returns cursor mode from edit mode +static MouseMode tui_mode2cursor(int mode) +{ + switch (mode) { + case INSERT: return SHAPE_IDX_I; + case CMDLINE: return SHAPE_IDX_C; + case REPLACE: return SHAPE_IDX_R; + case NORMAL: + default: return SHAPE_IDX_N; + } } +/// @param mode editor mode static void tui_mode_change(UI *ui, int mode) { TUIData *data = ui->data; if (mode == INSERT) { if (data->showing_mode != INSERT) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_bar); + tui_set_cursor(ui, SHAPE_IDX_I); } } else if (mode == CMDLINE) { if (data->showing_mode != CMDLINE) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_bar); + tui_set_cursor(ui, SHAPE_IDX_C); } } else if (mode == REPLACE) { if (data->showing_mode != REPLACE) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_ul); + tui_set_cursor(ui, SHAPE_IDX_R); } } else { assert(mode == NORMAL); if (data->showing_mode != NORMAL) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_block); + tui_set_cursor(ui, SHAPE_IDX_N); } } data->showing_mode = mode; } static void tui_set_scroll_region(UI *ui, int top, int bot, int left, - int right) + int right) { TUIData *data = ui->data; ugrid_set_scroll_region(&data->grid, top, bot, left, right); @@ -827,8 +961,6 @@ static void fix_terminfo(TUIData *data) goto end; } - bool inside_tmux = os_getenv("TMUX") != NULL; - #define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1)) if (STARTS_WITH(term, "rxvt")) { @@ -886,42 +1018,10 @@ static void fix_terminfo(TUIData *data) unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB); } - const char * env_cusr_shape = os_getenv("NVIM_TUI_ENABLE_CURSOR_SHAPE"); - if (env_cusr_shape && strncmp(env_cusr_shape, "0", 1) == 0) { - goto end; - } - bool cusr_blink = env_cusr_shape && strncmp(env_cusr_shape, "2", 1) == 0; - -#define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) - // Support changing cursor shape on some popular terminals. - const char *term_prog = os_getenv("TERM_PROGRAM"); - const char *vte_version = os_getenv("VTE_VERSION"); - - if ((term_prog && !strcmp(term_prog, "Konsole")) - || os_getenv("KONSOLE_DBUS_SESSION") != NULL) { - // Konsole uses a proprietary escape code to set the cursor shape - // and does not support DECSCUSR. - data->unibi_ext.set_cursor_shape_bar = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b]50;CursorShape=1\x07")); - data->unibi_ext.set_cursor_shape_ul = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b]50;CursorShape=2\x07")); - data->unibi_ext.set_cursor_shape_block = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b]50;CursorShape=0\x07")); - } else if (!vte_version || atoi(vte_version) >= 3900) { - // Assume that the terminal supports DECSCUSR unless it is an - // old VTE based terminal. This should not get wrapped for tmux, - // which will handle it via its Ss/Se terminfo extension - usually - // according to its terminal-overrides. - data->unibi_ext.set_cursor_shape_bar = - (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[5 q" : "\x1b[6 q"); - data->unibi_ext.set_cursor_shape_ul = - (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[3 q" : "\x1b[4 q"); - data->unibi_ext.set_cursor_shape_block = - (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[1 q" : "\x1b[2 q"); - } - end: // Fill some empty slots with common terminal strings + data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str( + ut, NULL, "\033]12;#%p1%06x\007"); data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, NULL, "\x1b[?1002h\x1b[?1006h"); data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, NULL, @@ -1006,16 +1106,16 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, stty_erase = tui_get_stty_erase(); } - if (strcmp(name, "key_backspace") == 0) { + if (strequal(name, "key_backspace")) { ILOG("libtermkey:kbs=%s", value); if (stty_erase != NULL && stty_erase[0] != 0) { return stty_erase; } - } else if (strcmp(name, "key_dc") == 0) { + } else if (strequal(name, "key_dc")) { ILOG("libtermkey:kdch1=%s", value); // Vim: "If <BS> and <DEL> are now the same, redefine <DEL>." - if (stty_erase != NULL && value != NULL && strcmp(stty_erase, value) == 0) { - return stty_erase[0] == DEL ? (char *)CTRL_H_STR : (char *)DEL_STR; + if (stty_erase != NULL && value != NULL && strequal(stty_erase, value)) { + return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR; } } diff --git a/src/nvim/tui/tui.h b/src/nvim/tui/tui.h index 07523bc124..2915b0e2f8 100644 --- a/src/nvim/tui/tui.h +++ b/src/nvim/tui/tui.h @@ -1,6 +1,8 @@ #ifndef NVIM_TUI_TUI_H #define NVIM_TUI_TUI_H +#include "nvim/cursor_shape.h" + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.h.generated.h" #endif diff --git a/src/nvim/ui.c b/src/nvim/ui.c index ea42e3e357..28f71b7ef2 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -29,6 +29,7 @@ #include "nvim/screen.h" #include "nvim/syntax.h" #include "nvim/window.h" +#include "nvim/cursor_shape.h" #ifdef FEAT_TUI # include "nvim/tui/tui.h" #else @@ -179,6 +180,7 @@ void ui_refresh(void) row = col = 0; screen_resize(width, height); pum_set_external(pum_external); + ui_cursor_style_set(); } static void ui_refresh_event(void **argv) @@ -376,6 +378,14 @@ void ui_cursor_goto(int new_row, int new_col) pending_cursor_update = true; } +void ui_cursor_style_set(void) +{ + Dictionary style = cursor_shape_dict(); + bool enabled = (*p_guicursor != NUL); + UI_CALL(cursor_style_set, enabled, style); + api_free_dictionary(style); +} + void ui_update_menu(void) { UI_CALL(update_menu); diff --git a/src/nvim/ui.h b/src/nvim/ui.h index d14bc5812c..8ffc5a45a6 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -22,6 +22,7 @@ struct ui_t { void (*clear)(UI *ui); void (*eol_clear)(UI *ui); void (*cursor_goto)(UI *ui, int row, int col); + void (*cursor_style_set)(UI *ui, bool enabled, Dictionary cursor_styles); void (*update_menu)(UI *ui); void (*busy_start)(UI *ui); void (*busy_stop)(UI *ui); diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index 25861abc1b..9f780663ac 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -13,6 +13,7 @@ #include "nvim/memory.h" #include "nvim/ui_bridge.h" #include "nvim/ugrid.h" +#include "nvim/api/private/helpers.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_bridge.c.generated.h" @@ -59,6 +60,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler) rv->bridge.clear = ui_bridge_clear; rv->bridge.eol_clear = ui_bridge_eol_clear; rv->bridge.cursor_goto = ui_bridge_cursor_goto; + rv->bridge.cursor_style_set = ui_bridge_cursor_style_set; rv->bridge.update_menu = ui_bridge_update_menu; rv->bridge.busy_start = ui_bridge_busy_start; rv->bridge.busy_stop = ui_bridge_busy_stop; @@ -178,6 +180,25 @@ static void ui_bridge_cursor_goto_event(void **argv) ui->cursor_goto(ui, PTR2INT(argv[1]), PTR2INT(argv[2])); } +static void ui_bridge_cursor_style_set(UI *b, bool enabled, Dictionary styles) +{ + bool *enabledp = xmalloc(sizeof(*enabledp)); + Object *stylesp = xmalloc(sizeof(*stylesp)); + *enabledp = enabled; + *stylesp = copy_object(DICTIONARY_OBJ(styles)); + UI_CALL(b, cursor_style_set, 3, b, enabledp, stylesp); +} +static void ui_bridge_cursor_style_set_event(void **argv) +{ + UI *ui = UI(argv[0]); + bool *enabled = argv[1]; + Object *styles = argv[2]; + ui->cursor_style_set(ui, *enabled, styles->data.dictionary); + xfree(enabled); + api_free_object(*styles); + xfree(styles); +} + static void ui_bridge_update_menu(UI *b) { UI_CALL(b, update_menu, 1, b); diff --git a/src/nvim/undo.c b/src/nvim/undo.c index c95a795587..4d4e8d9bb9 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -82,6 +82,7 @@ #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/undo.h" +#include "nvim/macros.h" #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" @@ -317,7 +318,7 @@ static long get_undolevel(void) static inline void zero_fmark_additional_data(fmark_T *fmarks) { for (size_t i = 0; i < NMARKS; i++) { - dict_unref(fmarks[i].additional_data); + tv_dict_unref(fmarks[i].additional_data); fmarks[i].additional_data = NULL; } } @@ -1080,7 +1081,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, */ perm = 0600; if (buf->b_ffname != NULL) { - perm = os_getperm(buf->b_ffname); + perm = os_getperm((const char *)buf->b_ffname); if (perm < 0) { perm = 0600; } @@ -1139,7 +1140,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, EMSG2(_(e_not_open), file_name); goto theend; } - (void)os_setperm((char_u *)file_name, perm); + (void)os_setperm(file_name, perm); if (p_verbose > 0) { verbose_enter(); smsg(_("Writing undo file: %s"), file_name); @@ -1164,7 +1165,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, && 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)) { - os_setperm((char_u *)file_name, (perm & 0707) | ((perm & 07) << 3)); + os_setperm(file_name, (perm & 0707) | ((perm & 07) << 3)); } # ifdef HAVE_SELINUX if (buf->b_ffname != NULL) @@ -2941,25 +2942,28 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) dict_T *dict; while (uhp != NULL) { - dict = dict_alloc(); - dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL); - dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL); - if (uhp == curbuf->b_u_newhead) - dict_add_nr_str(dict, "newhead", 1, NULL); - if (uhp == curbuf->b_u_curhead) - dict_add_nr_str(dict, "curhead", 1, NULL); - if (uhp->uh_save_nr > 0) - dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL); + dict = tv_dict_alloc(); + tv_dict_add_nr(dict, S_LEN("seq"), (varnumber_T)uhp->uh_seq); + tv_dict_add_nr(dict, S_LEN("time"), (varnumber_T)uhp->uh_time); + if (uhp == curbuf->b_u_newhead) { + tv_dict_add_nr(dict, S_LEN("newhead"), 1); + } + if (uhp == curbuf->b_u_curhead) { + tv_dict_add_nr(dict, S_LEN("curhead"), 1); + } + if (uhp->uh_save_nr > 0) { + tv_dict_add_nr(dict, S_LEN("save"), (varnumber_T)uhp->uh_save_nr); + } if (uhp->uh_alt_next.ptr != NULL) { - list_T *alt_list = list_alloc(); + list_T *alt_list = tv_list_alloc(); - /* Recursive call to add alternate undo tree. */ + // Recursive call to add alternate undo tree. u_eval_tree(uhp->uh_alt_next.ptr, alt_list); - dict_add_list(dict, "alt", alt_list); + tv_dict_add_list(dict, S_LEN("alt"), alt_list); } - list_append_dict(list, dict); + tv_list_append_dict(list, dict); uhp = uhp->uh_prev.ptr; } } diff --git a/src/nvim/version.c b/src/nvim/version.c index 91c37658aa..ca520c7af5 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -73,51 +73,51 @@ static char *features[] = { }; // clang-format off -static int included_patches[] = { - // 2367, +static const int included_patches[] = { + // 2367,NA // 2366 NA // 2365 NA - // 2364, + // 2364,NA // 2363 NA 2362, // 2361 NA // 2360, // 2359 NA // 2358 NA - // 2357, + 2357, // 2356, - // 2355, + 2355, // 2354, 2353, // 2352 NA // 2351 NA // 2350, - // 2349, + 2349, 2348, 2347, - // 2346, + 2346, // 2345 NA // 2344 NA - // 2343, + 2343, // 2342 NA - // 2341, + 2341, // 2340 NA - // 2339, + 2339, // 2338 NA 2337, 2336, 2335, - // 2334, + 2334, 2333, // 2332 NA 2331, - // 2330, - // 2329, - // 2328, + 2330, + 2329, + 2328, // 2327 NA 2326, // 2325 NA - // 2324, + 2324, 2323, 2322, 2321, @@ -134,23 +134,23 @@ static int included_patches[] = { // 2310 NA 2309, // 2308 NA - // 2307, - // 2306, + 2307, + 2306, 2305, // 2304 NA - // 2303, + 2303, // 2302 NA // 2301 NA 2300, 2299, // 2298 NA // 2297 NA - // 2296, + 2296, 2295, 2294, - // 2293, + 2293, 2292, - // 2291, + 2291, // 2290 NA // 2289 NA // 2288 NA @@ -158,24 +158,24 @@ static int included_patches[] = { // 2286 NA // 2285 NA 2284, - // 2283, + 2283, // 2282 NA // 2281 NA - // 2280, + 2280, 2279, // 2278 NA 2277, - // 2276, + 2276, 2275, 2274, 2273, 2272, // 2271 NA // 2270 NA - // 2269, + 2269, // 2268, // 2267 NA - // 2266, + 2266, 2265, 2264, // 2263, @@ -185,8 +185,8 @@ static int included_patches[] = { // 2259, // 2258 NA // 2257 NA - // 2256, - // 2255, + 2256, + 2255, // 2254 NA // 2253 NA // 2252 NA @@ -205,7 +205,7 @@ static int included_patches[] = { // 2239, // 2238 NA 2237, - // 2236, + 2236, 2235, // 2234 NA 2233, @@ -220,7 +220,7 @@ static int included_patches[] = { // 2224, 2223, 2222, - // 2221, + 2221, 2220, 2219, // 2218 NA @@ -257,30 +257,30 @@ static int included_patches[] = { 2187, // 2186 NA 2185, - // 2184, + 2184, 2183, // 2182 NA // 2181 NA 2180, // 2179, - // 2178, - // 2177, + 2178, + 2177, // 2176 NA 2175, 2174, // 2173, - // 2172, + 2172, // 2171 NA 2170, - // 2169, + 2169, // 2168 NA // 2167 NA // 2166 NA // 2165, - // 2164, + 2164, 2163, 2162, - // 2161, + 2161, 2160, 2159, 2158, @@ -337,7 +337,7 @@ static int included_patches[] = { 2107, 2106, // 2105 NA - // 2104, + 2104, 2103, // 2102 NA 2101, @@ -2461,10 +2461,10 @@ static char *(extra_patches[]) = { /// @param version Version string like "1.3.42" /// /// @return true if Nvim is at or above the version. -bool has_nvim_version(char *version_str) - FUNC_ATTR_NONNULL_ALL +bool has_nvim_version(const char *const version_str) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char *p = version_str; + const char *p = version_str; int major = 0; int minor = 0; int patch = 0; @@ -2473,7 +2473,7 @@ bool has_nvim_version(char *version_str) return false; } major = atoi(p); - p = strchr(p, '.'); // Find the next dot. + p = strchr(p, '.'); // Find the next dot. if (p) { p++; // Advance past the dot. @@ -2481,7 +2481,7 @@ bool has_nvim_version(char *version_str) return false; } minor = atoi(p); - p = strchr(p, '.'); + p = strchr(p, '.'); if (p) { p++; if (!ascii_isdigit(*p)) { diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 458d23fcad..cc0587fb88 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -30,7 +30,7 @@ Error: configure did not run properly.Check auto/config.log. #include "nvim/os/os_defs.h" /* bring lots of system header files */ /// length of a buffer to store a number in ASCII (64 bits binary + NUL) -#define NUMBUFLEN 65 +enum { NUMBUFLEN = 65 }; // flags for vim_str2nr() #define STR2NR_BIN 1 @@ -51,22 +51,7 @@ Error: configure did not run properly.Check auto/config.log. /* ================ end of the header file puzzle =============== */ -#ifdef HAVE_WORKING_LIBINTL -# include <libintl.h> -# define _(x) gettext((char *)(x)) -// XXX do we actually need this? -# ifdef gettext_noop -# define N_(x) gettext_noop(x) -# else -# define N_(x) x -# endif -#else -# define _(x) ((char *)(x)) -# define N_(x) x -# define bindtextdomain(x, y) /* empty */ -# define bind_textdomain_codeset(x, y) /* empty */ -# define textdomain(x) /* empty */ -#endif +#include "nvim/gettext.h" /* special attribute addition: Put message in history */ #define MSG_HIST 0x1000 @@ -109,11 +94,14 @@ Error: configure did not run properly.Check auto/config.log. // all mode bits used for mapping #define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS) -/* directions */ -#define FORWARD 1 -#define BACKWARD (-1) -#define FORWARD_FILE 3 -#define BACKWARD_FILE (-3) +/// Directions. +typedef enum { + kDirectionNotSet = 0, + FORWARD = 1, + BACKWARD = (-1), + FORWARD_FILE = 3, + BACKWARD_FILE = (-3), +} Direction; /* return values for functions */ #if !(defined(OK) && (OK == 1)) @@ -282,15 +270,22 @@ enum { #define SHOWCMD_COLS 10 /* columns needed by shown command */ #define STL_MAX_ITEM 80 /* max nr of %<flag> in statusline */ -/* - * fnamecmp() is used to compare file names. - * On some systems case in a file name does not matter, on others it does. - * (this does not account for maximum name lengths and things like "../dir", - * thus it is not 100% accurate!) - */ -#define fnamecmp(x, y) vim_fnamecmp((char_u *)(x), (char_u *)(y)) -#define fnamencmp(x, y, n) vim_fnamencmp((char_u *)(x), (char_u *)(y), \ - (size_t)(n)) +/// Compare file names +/// +/// On some systems case in a file name does not matter, on others it does. +/// +/// @note Does not account for maximum name lengths and things like "../dir", +/// thus it is not 100% accurate. OS may also use different algorythm for +/// case-insensitive comparison. +/// +/// @param[in] x First file name to compare. +/// @param[in] y Second file name to compare. +/// +/// @return 0 for equal file names, non-zero otherwise. +#define fnamecmp(x, y) path_fnamecmp((const char *)(x), (const char *)(y)) +#define fnamencmp(x, y, n) path_fnamencmp((const char *)(x), \ + (const char *)(y), \ + (size_t)(n)) /* * Enums need a typecast to be used as array index (for Ultrix). @@ -325,6 +320,8 @@ enum { #define DIP_START 0x08 // also use "start" directory in 'packpath' #define DIP_OPT 0x10 // also use "opt" directory in 'packpath' #define DIP_NORTP 0x20 // do not use 'runtimepath' +#define DIP_NOAFTER 0x40 // skip "after" directories +#define DIP_AFTER 0x80 // only use "after" directories // Lowest number used for window ID. Cannot have this many windows per tab. #define LOWEST_WIN_ID 1000 diff --git a/src/nvim/window.c b/src/nvim/window.c index 6c9d3554f1..6020159af9 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -447,13 +447,12 @@ wingotofile: case 'g': case Ctrl_G: CHECK_CMDWIN - ++ no_mapping; - ++allow_keys; /* no mapping for xchar, but allow key codes */ - if (xchar == NUL) + no_mapping++; + if (xchar == NUL) { xchar = plain_vgetc(); - LANGMAP_ADJUST(xchar, TRUE); - --no_mapping; - --allow_keys; + } + LANGMAP_ADJUST(xchar, true); + no_mapping--; (void)add_to_showcmd(xchar); switch (xchar) { case '}': @@ -1714,14 +1713,10 @@ static void win_equal_rec( } } -/* - * close all windows for buffer 'buf' - */ -void -close_windows ( - buf_T *buf, - int keep_curwin /* don't close "curwin" */ -) +/// Closes all windows for buffer `buf`. +/// +/// @param keep_curwin don't close `curwin` +void close_windows(buf_T *buf, int keep_curwin) { tabpage_T *tp, *nexttp; int h = tabline_height(); @@ -1731,9 +1726,11 @@ close_windows ( for (win_T *wp = firstwin; wp != NULL && lastwin != firstwin; ) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) - && !(wp->w_closing || wp->w_buffer->b_closing) - ) { - win_close(wp, FALSE); + && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { + if (win_close(wp, false) == FAIL) { + // If closing the window fails give up, to avoid looping forever. + break; + } /* Start all over, autocommands may change the window layout. */ wp = firstwin; @@ -1747,9 +1744,8 @@ close_windows ( if (tp != curtab) { FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (wp->w_buffer == buf - && !(wp->w_closing || wp->w_buffer->b_closing) - ) { - win_close_othertab(wp, FALSE, tp); + && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { + win_close_othertab(wp, false, tp); /* Start all over, the tab page may be closed and * autocommands may change the window layout. */ @@ -1884,8 +1880,10 @@ int win_close(win_T *win, int free_buf) return FAIL; } - if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) - return FAIL; /* window is already being closed */ + if (win->w_closing + || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { + return FAIL; // window is already being closed + } if (win == aucmd_win) { EMSG(_("E813: Cannot close autocmd window")); return FAIL; @@ -2019,10 +2017,12 @@ int win_close(win_T *win, int free_buf) } curbuf = curwin->w_buffer; close_curwin = TRUE; + + // The cursor position may be invalid if the buffer changed after last + // using the window. + check_cursor(); } - if (p_ea - && (*p_ead == 'b' || *p_ead == dir) - ) { + if (p_ea && (*p_ead == 'b' || *p_ead == dir)) { win_equal(curwin, true, dir); } else { win_comp_pos(); @@ -2066,7 +2066,8 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) // Get here with win->w_buffer == NULL when win_close() detects the tab page // changed. - if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) { + if (win->w_closing + || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { return; // window is already being closed } @@ -2982,8 +2983,8 @@ static tabpage_T *alloc_tabpage(void) tp->handle = ++last_tp_handle; handle_register_tabpage(tp); - /* init t: variables */ - tp->tp_vars = dict_alloc(); + // Init t: variables. + tp->tp_vars = tv_dict_alloc(); init_var_dict(tp->tp_vars, &tp->tp_winvar, VAR_SCOPE); tp->tp_diff_invalid = TRUE; tp->tp_ch_used = p_ch; @@ -3134,6 +3135,45 @@ bool valid_tabpage(tabpage_T *tpc) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return false; } +/// Returns true when `tpc` is valid and at least one window is valid. +int valid_tabpage_win(tabpage_T *tpc) +{ + FOR_ALL_TABS(tp) { + if (tp == tpc) { + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + if (win_valid_any_tab(wp)) { + return true; + } + } + return false; + } + } + // shouldn't happen + return false; +} + +/// Close tabpage `tab`, assuming it has no windows in it. +/// There must be another tabpage or this will crash. +void close_tabpage(tabpage_T *tab) +{ + tabpage_T *ptp; + + if (tab == first_tabpage) { + first_tabpage = tab->tp_next; + ptp = first_tabpage; + } else { + for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tab; + ptp = ptp->tp_next) { + // do nothing + } + assert(ptp != NULL); + ptp->tp_next = tab->tp_next; + } + + goto_tabpage_tp(ptp, false, false); + free_tabpage(tab); +} + /* * Find tab page "n" (first one is 1). Returns NULL when not found. */ @@ -3771,8 +3811,8 @@ static win_T *win_alloc(win_T *after, int hidden) new_wp->handle = ++last_win_id; handle_register_window(new_wp); - /* init w: variables */ - new_wp->w_vars = dict_alloc(); + // Init w: variables. + new_wp->w_vars = tv_dict_alloc(); init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE); /* Don't execute autocommands while the window is not properly @@ -4762,7 +4802,11 @@ void win_new_height(win_T *wp, int height) wp->w_height = height; wp->w_skipcol = 0; - scroll_to_fraction(wp, prev_height); + // There is no point in adjusting the scroll position when exiting. Some + // values might be invalid. + if (!exiting) { + scroll_to_fraction(wp, prev_height); + } } void scroll_to_fraction(win_T *wp, int prev_height) @@ -5325,10 +5369,8 @@ restore_snapshot ( clear_snapshot(curtab, idx); } -/* - * Check if frames "sn" and "fr" have the same layout, same following frames - * and same children. - */ +/// Check if frames "sn" and "fr" have the same layout, same following frames +/// and same children. And the window pointer is valid. static int check_snapshot_rec(frame_T *sn, frame_T *fr) { if (sn->fr_layout != fr->fr_layout @@ -5337,7 +5379,8 @@ static int check_snapshot_rec(frame_T *sn, frame_T *fr) || (sn->fr_next != NULL && check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL) || (sn->fr_child != NULL - && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL)) + && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL) + || (sn->fr_win != NULL && !win_valid(sn->fr_win))) return FAIL; return OK; } @@ -5461,9 +5504,9 @@ void restore_buffer(bufref_T *save_curbuf) // Optionally, a desired ID 'id' can be specified (greater than or equal to 1). // If no particular ID is desired, -1 must be specified for 'id'. // Return ID of added match, -1 on failure. -int match_add(win_T *wp, char_u *grp, char_u *pat, +int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, list_T *pos_list, - char_u *conceal_char) + const char *const conceal_char) { matchitem_T *cur; matchitem_T *prev; @@ -5491,11 +5534,11 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, cur = cur->next; } } - if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0) { + if ((hlg_id = syn_name2id((const char_u *)grp)) == 0) { EMSG2(_(e_nogroup), grp); return -1; } - if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) { + if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) { EMSG2(_(e_invarg2), pat); return -1; } @@ -5514,14 +5557,14 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, m = xcalloc(1, sizeof(matchitem_T)); m->id = id; m->priority = prio; - m->pattern = pat == NULL ? NULL: vim_strsave(pat); + m->pattern = pat == NULL ? NULL: (char_u *)xstrdup(pat); m->hlg_id = hlg_id; m->match.regprog = regprog; m->match.rmm_ic = FALSE; m->match.rmm_maxcol = 0; m->conceal_char = 0; if (conceal_char != NULL) { - m->conceal_char = (*mb_ptr2char)(conceal_char); + m->conceal_char = (*mb_ptr2char)((const char_u *)conceal_char); } // Set up position matches @@ -5539,7 +5582,7 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int len = 1; list_T *subl; listitem_T *subli; - int error = false; + bool error = false; if (li->li_tv.v_type == VAR_LIST) { subl = li->li_tv.vval.v_list; @@ -5550,8 +5593,8 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, if (subli == NULL) { goto fail; } - lnum = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) { + lnum = tv_get_number_chk(&subli->li_tv, &error); + if (error) { goto fail; } if (lnum == 0) { @@ -5561,13 +5604,14 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, m->pos.pos[i].lnum = lnum; subli = subli->li_next; if (subli != NULL) { - col = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) + col = tv_get_number_chk(&subli->li_tv, &error); + if (error) { goto fail; + } subli = subli->li_next; if (subli != NULL) { - len = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) { + len = tv_get_number_chk(&subli->li_tv, &error); + if (error) { goto fail; } } @@ -5766,14 +5810,14 @@ int win_getid(typval_T *argvars) if (argvars[0].v_type == VAR_UNKNOWN) { return curwin->handle; } - int winnr = get_tv_number(&argvars[0]); + int winnr = tv_get_number(&argvars[0]); win_T *wp; if (winnr > 0) { if (argvars[1].v_type == VAR_UNKNOWN) { wp = firstwin; } else { tabpage_T *tp = NULL; - int tabnr = get_tv_number(&argvars[1]); + int tabnr = tv_get_number(&argvars[1]); FOR_ALL_TABS(tp2) { if (--tabnr == 0) { tp = tp2; @@ -5783,7 +5827,11 @@ int win_getid(typval_T *argvars) if (tp == NULL) { return -1; } - wp = tp->tp_firstwin; + if (tp == curtab) { + wp = firstwin; + } else { + wp = tp->tp_firstwin; + } } for ( ; wp != NULL; wp = wp->w_next) { if (--winnr == 0) { @@ -5796,7 +5844,7 @@ int win_getid(typval_T *argvars) int win_gotoid(typval_T *argvars) { - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { @@ -5831,16 +5879,16 @@ void win_id2tabwin(typval_T *argvars, list_T *list) { int winnr = 1; int tabnr = 1; - int id = get_tv_number(&argvars[0]); + handle_T id = (handle_T)tv_get_number(&argvars[0]); win_get_tabwin(id, &tabnr, &winnr); - list_append_number(list, tabnr); - list_append_number(list, winnr); + tv_list_append_number(list, tabnr); + tv_list_append_number(list, winnr); } win_T * win_id2wp(typval_T *argvars) { - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { @@ -5854,7 +5902,7 @@ win_T * win_id2wp(typval_T *argvars) int win_id2win(typval_T *argvars) { int nr = 1; - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->handle == id) { @@ -5867,11 +5915,11 @@ int win_id2win(typval_T *argvars) void win_findbuf(typval_T *argvars, list_T *list) { - int bufnr = get_tv_number(&argvars[0]); + int bufnr = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer->b_fnum == bufnr) { - list_append_number(list, wp->handle); + tv_list_append_number(list, wp->handle); } } } |