diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-02-02 19:02:58 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-02-02 19:02:58 +0000 |
commit | b255fa570d8b041e4c81e3454d51e06100c2fa4f (patch) | |
tree | d3b246b467500ca48067ed4a45d2fa53966cd9f1 | |
parent | eeccad2ff1ae8892fe9e06d733a7b07a166eecb0 (diff) | |
parent | 0bd07bea095a8000cffa4f379c1fa53e009c1143 (diff) | |
download | rneovim-20230125_mix.tar.gz rneovim-20230125_mix.tar.bz2 rneovim-20230125_mix.zip |
Merge branch 'aucmd_textputpost' into 20230125_mix20230125_mix
110 files changed, 2006 insertions, 1646 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b158d966e1..83ee4f0358 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,7 +128,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup common environment variables - run: ./.github/workflows/env.sh lint + run: ./.github/workflows/env.sh lintc - name: Install apt packages run: | @@ -141,14 +141,17 @@ jobs: libtree-sitter-dev \ libunibilium-dev \ libuv1-dev \ - libvterm-dev \ lua-busted \ lua-filesystem \ lua-inspect \ lua-lpeg \ - lua-luv-dev \ lua-nvim \ luajit + # libvterm-dev \ + # lua-luv-dev + + # Remove comments from packages once we start using these external + # dependencies. See env.sh for more context. - uses: ./.github/actions/cache @@ -156,7 +159,7 @@ jobs: run: ./ci/before_script.sh - name: Build nvim - run: ./ci/run_tests.sh build_nvim + run: make - if: "!cancelled()" name: Determine if run should be aborted diff --git a/.github/workflows/env.sh b/.github/workflows/env.sh index d93552fed3..42a355da44 100755 --- a/.github/workflows/env.sh +++ b/.github/workflows/env.sh @@ -26,7 +26,6 @@ BUILD_FLAGS="CMAKE_FLAGS=-DCI_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_ case "$FLAVOR" in asan) - BUILD_FLAGS="$BUILD_FLAGS -DPREFER_LUA=ON" cat <<EOF >> "$GITHUB_ENV" CLANG_SANITIZER=ASAN_UBSAN ASAN_OPTIONS=detect_leaks=1:check_initialization_order=1:log_path=$GITHUB_WORKSPACE/build/log/asan:intercept_tls_get_addr=0 @@ -44,13 +43,15 @@ EOF BUILD_UCHAR=1 EOF ;; - lint) + lintc) # Re-enable once system deps are available # BUILD_FLAGS="$BUILD_FLAGS -DLIBLUV_LIBRARY:FILEPATH=/usr/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/lua/5.1/luv.so -DLIBLUV_INCLUDE_DIR:PATH=/usr/include/lua5.1" - DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -DUSE_BUNDLED_LUV=ON" - cat <<EOF >> "$GITHUB_ENV" -USE_BUNDLED=OFF -EOF + + # Ideally all dependencies should external for this job, but some + # dependencies don't have the required version available. We use the + # bundled versions for these with the hopes of being able to remove them + # later on. + DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -DUSE_BUNDLED=OFF -DUSE_BUNDLED_LUV=ON -DUSE_BUNDLED_LIBVTERM=ON" ;; functionaltest-lua) BUILD_FLAGS="$BUILD_FLAGS -DPREFER_LUA=ON" diff --git a/.github/workflows/news.yml b/.github/workflows/news.yml index 46ac0ec02d..11807e9b42 100644 --- a/.github/workflows/news.yml +++ b/.github/workflows/news.yml @@ -22,9 +22,10 @@ jobs: { echo " Pull request includes a new feature or a breaking change, but - news.txt hasn't been updated yet. news.txt is our primary way of - communicating changes to users so it's important to keep it up to - date." + news.txt hasn't been updated yet. This is just a reminder + that news.txt may need to be updated. You can ignore this CI + failure if you think the change won't be of interest to + users." exit 1 } fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 493c212996..9c0d999ab8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,11 @@ set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches) set_property(GLOBAL PROPERTY USE_FOLDERS ON) +find_program(CCACHE_PRG ccache) +if(CCACHE_PRG) + set(CMAKE_C_COMPILER_LAUNCHER ${CMAKE_COMMAND} -E env CCACHE_SLOPPINESS=pch_defines,time_macros ${CCACHE_PRG}) +endif() + # Prefer our bundled versions of dependencies. if(DEFINED ENV{DEPS_BUILD_DIR}) if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD") @@ -112,11 +117,6 @@ endif() message(STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") set_default_buildtype() -if(CMAKE_BUILD_TYPE MATCHES Debug) - set(DEBUG 1) -else() - set(DEBUG 0) -endif() # If not in a git repo (e.g., a tarball) these tokens define the complete # version string, else they are combined with the result of `git describe`. @@ -370,7 +370,7 @@ endif() if(BUSTED_PRG) get_target_property(TEST_INCLUDE_DIRS main_lib INTERFACE_INCLUDE_DIRECTORIES) - set(UNITTEST_PREREQS nvim-test) + set(UNITTEST_PREREQS nvim) set(FUNCTIONALTEST_PREREQS nvim printenv-test printargs-test shell-test pwsh-test streams-test tty-test ${GENERATED_HELP_TAGS}) set(BENCHMARK_PREREQS nvim tty-test) @@ -380,6 +380,7 @@ if(BUSTED_PRG) COMMAND ${CMAKE_COMMAND} -DBUSTED_PRG=${BUSTED_PRG} -DLUA_PRG=${LUA_PRG} + -DNVIM_PRG=$<TARGET_FILE:nvim> -DWORKING_DIR=${CMAKE_CURRENT_SOURCE_DIR} -DBUSTED_OUTPUT_TYPE=${BUSTED_OUTPUT_TYPE} -DTEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test @@ -394,17 +395,9 @@ if(BUSTED_PRG) message(WARNING "disabling unit tests: no Luajit FFI in ${LUA_PRG}") endif() - if(LUA_HAS_FFI) - set(TEST_LIBNVIM_PATH $<TARGET_FILE:nvim-test>) - else() - set(TEST_LIBNVIM_PATH "") - endif() configure_file( ${CMAKE_SOURCE_DIR}/test/cmakeconfig/paths.lua.in - ${CMAKE_BINARY_DIR}/test/cmakeconfig/paths.lua.gen) - file(GENERATE - OUTPUT ${CMAKE_BINARY_DIR}/test/cmakeconfig/paths.lua - INPUT ${CMAKE_BINARY_DIR}/test/cmakeconfig/paths.lua.gen) + ${CMAKE_BINARY_DIR}/test/cmakeconfig/paths.lua) add_custom_target(functionaltest COMMAND ${CMAKE_COMMAND} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ce0f2ace05..f1c7ca1cb3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,6 +42,11 @@ Developer guidelines make distclean make # Nvim build system uses ninja automatically, if available. ``` +- Install `ccache` for faster rebuilds of Nvim. Nvim will use it automatically + if it's found. To disable caching use: + ``` + CCACHE_DISABLE=true make + ``` Pull requests (PRs) --------------------- @@ -132,7 +132,7 @@ functionaltest-lua: | nvim FORMAT=formatc formatlua format LINT=lintlua lintsh lintc check-single-includes lintcommit lint TEST=functionaltest unittest -generated-sources benchmark $(FORMAT) $(LINT) $(TEST): | build/.ran-cmake +generated-sources benchmark uninstall $(FORMAT) $(LINT) $(TEST): | build/.ran-cmake $(CMAKE_PRG) --build build --target $@ test: $(TEST) @@ -173,4 +173,4 @@ $(DEPS_BUILD_DIR)/%: phony_force $(BUILD_TOOL) -C $(DEPS_BUILD_DIR) $(patsubst $(DEPS_BUILD_DIR)/%,%,$@) endif -.PHONY: test clean distclean nvim libnvim cmake deps install appimage checkprefix benchmark $(FORMAT) $(LINT) $(TEST) +.PHONY: test clean distclean nvim libnvim cmake deps install appimage checkprefix benchmark uninstall $(FORMAT) $(LINT) $(TEST) diff --git a/ci/common/build.sh b/ci/common/build.sh index e30d0337b5..95972aab13 100644 --- a/ci/common/build.sh +++ b/ci/common/build.sh @@ -16,8 +16,7 @@ build_make() { } build_deps() { - if test "${FUNCTIONALTEST}" = "functionaltest-lua" \ - || test "${CLANG_SANITIZER}" = "ASAN_UBSAN" ; then + if test "${FUNCTIONALTEST}" = "functionaltest-lua" ; then DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} -DUSE_BUNDLED_LUA=ON" fi @@ -66,13 +65,6 @@ build_nvim() { if ! top_make libnvim ; then exit 1 fi - - if test "${FUNCTIONALTEST}" != "functionaltest-lua"; then - echo "Building nvim-test." - if ! top_make nvim-test ; then - exit 1 - fi - fi fi # Invoke nvim to trigger *San early. diff --git a/cmake.config/config.h.in b/cmake.config/config.h.in index c8377bf45c..4669e42c0f 100644 --- a/cmake.config/config.h.in +++ b/cmake.config/config.h.in @@ -1,8 +1,6 @@ #ifndef AUTO_CONFIG_H #define AUTO_CONFIG_H -#cmakedefine DEBUG - #cmakedefine SIZEOF_INT @SIZEOF_INT@ #cmakedefine SIZEOF_INTMAX_T @SIZEOF_INTMAX_T@ #cmakedefine SIZEOF_INT32_T @SIZEOF_INT32_T@ diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt index a7e71ffc92..90ae91463d 100644 --- a/cmake.deps/CMakeLists.txt +++ b/cmake.deps/CMakeLists.txt @@ -153,8 +153,8 @@ set(LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333) set(LUAROCKS_URL https://github.com/luarocks/luarocks/archive/v3.8.0.tar.gz) set(LUAROCKS_SHA256 ab6612ca9ab87c6984871d2712d05525775e8b50172701a0a1cabddf76de2be7) -set(UNIBILIUM_URL https://github.com/neovim/unibilium/archive/92d929f.tar.gz) -set(UNIBILIUM_SHA256 29815283c654277ef77a3adcc8840db79ddbb20a0f0b0c8f648bd8cd49a02e4b) +set(UNIBILIUM_URL https://github.com/neovim/unibilium/archive/d72c3598e7ac5d1ebf86ee268b8b4ed95c0fa628.tar.gz) +set(UNIBILIUM_SHA256 9c4747c862ab5e3076dcf8fa8f0ea7a6b50f20ec5905618b9536655596797487) set(LIBTERMKEY_URL https://www.leonerd.org.uk/code/libtermkey/libtermkey-0.22.tar.gz) set(LIBTERMKEY_SHA256 6945bd3c4aaa83da83d80a045c5563da4edd7d0374c62c0d35aec09eb3014600) @@ -194,8 +194,8 @@ set(TREESITTER_LUA_SHA256 930d0370dc15b66389869355c8e14305b9ba7aafd36edbfdb468c8 set(TREESITTER_VIM_URL https://github.com/vigoux/tree-sitter-viml/archive/55ff1b080c09edeced9b748cf4c16d0b49d17fb9.tar.gz) set(TREESITTER_VIM_SHA256 1b1cd39e33c8fb02fa7fe3977e844883c2a8508a7edd621f2d21e39a9aeefa92) -set(TREESITTER_HELP_URL https://github.com/neovim/tree-sitter-vimdoc/archive/ce20f13c3f12506185754888feaae3f2ad54c287.tar.gz) -set(TREESITTER_HELP_SHA256 2b8b166438cce66064aab56a744430b1f44871f43e47f70b51246d14bb826609) +set(TREESITTER_HELP_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v1.3.0.tar.gz) +set(TREESITTER_HELP_SHA256 f33f6d49c7d71feb2fd68ef2b2684da150f9f8e486ad9726213631d673942331) set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/eb970a83a107cbaba52e31588355c0b09b3a308a.tar.gz) set(TREESITTER_SHA256 58063721182f182fb9ec5481794f2ba594e52d6c2497e0214570535054dfc78d) diff --git a/cmake.deps/cmake/BuildLibvterm.cmake b/cmake.deps/cmake/BuildLibvterm.cmake index 1578d56fba..b75987eb24 100644 --- a/cmake.deps/cmake/BuildLibvterm.cmake +++ b/cmake.deps/cmake/BuildLibvterm.cmake @@ -1,27 +1,3 @@ -if(WIN32) - set(LIBVTERM_CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibvtermCMakeLists.txt - ${DEPS_BUILD_DIR}/src/libvterm/CMakeLists.txt - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Libvterm-tbl2inc_c.cmake - ${DEPS_BUILD_DIR}/src/libvterm/tbl2inc_c.cmake - COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/libvterm - -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR} - -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} - -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} - -DCMAKE_GENERATOR=${CMAKE_GENERATOR} - -DCMAKE_POSITION_INDEPENDENT_CODE=ON) - set(LIBVTERM_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config $<CONFIG>) - set(LIBVTERM_INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config $<CONFIG>) -else() - set(LIBVTERM_INSTALL_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER} - PREFIX=${DEPS_INSTALL_DIR} - CFLAGS=-fPIC - LDFLAGS+=-static - ${DEFAULT_MAKE_CFLAGS} - install) -endif() - if(USE_EXISTING_SRC_DIR) unset(LIBVTERM_URL) endif() @@ -30,10 +6,9 @@ ExternalProject_Add(libvterm URL_HASH SHA256=${LIBVTERM_SHA256} DOWNLOAD_NO_PROGRESS TRUE DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libvterm - BUILD_IN_SOURCE 1 - CONFIGURE_COMMAND "${LIBVTERM_CONFIGURE_COMMAND}" - BUILD_COMMAND "${LIBVTERM_BUILD_COMMAND}" - INSTALL_COMMAND "${LIBVTERM_INSTALL_COMMAND}" + PATCH_COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibvtermCMakeLists.txt + ${DEPS_BUILD_DIR}/src/libvterm/CMakeLists.txt CMAKE_ARGS ${DEPS_CMAKE_ARGS} CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS}) diff --git a/cmake.deps/cmake/BuildLuarocks.cmake b/cmake.deps/cmake/BuildLuarocks.cmake index d0b4a8e7d1..b84ce34d45 100644 --- a/cmake.deps/cmake/BuildLuarocks.cmake +++ b/cmake.deps/cmake/BuildLuarocks.cmake @@ -148,7 +148,7 @@ if(USE_BUNDLED_BUSTED) set(LUACHECK_EXE "${DEPS_BIN_DIR}/luacheck") endif() add_custom_command(OUTPUT ${BUSTED_EXE} - COMMAND ${LUAROCKS_BINARY} build busted 2.0.0 ${LUAROCKS_BUILDARGS} + COMMAND ${LUAROCKS_BINARY} build busted 2.1.1 ${LUAROCKS_BUILDARGS} DEPENDS penlight) add_custom_target(busted DEPENDS ${BUSTED_EXE}) diff --git a/cmake.deps/cmake/Libvterm-tbl2inc_c.cmake b/cmake.deps/cmake/Libvterm-tbl2inc_c.cmake deleted file mode 100644 index 32d973680f..0000000000 --- a/cmake.deps/cmake/Libvterm-tbl2inc_c.cmake +++ /dev/null @@ -1,163 +0,0 @@ -cmake_minimum_required(VERSION 3.10) - -set(HEX_ALPHABET "0123456789abcdef") - -function(ConvertToHex dec hex) - while(dec GREATER 0) - math(EXPR _val "${dec} % 16") - math(EXPR dec "${dec} / 16") - string(SUBSTRING ${HEX_ALPHABET} ${_val} 1 _val) - set(_res "${_val}${_res}") - endwhile() - # Pad the result with the number of zeros - # specified by the optional third argument - if(${ARGC} EQUAL 3) - set(padding ${ARGV2}) - string(LENGTH ${_res} _resLen) - if(_resLen LESS ${padding}) - math(EXPR _neededPadding "${padding} - ${_resLen}") - foreach(i RANGE 1 ${_neededPadding}) - set(_res "0${_res}") - endforeach() - endif() - endif() - set(${hex} "0x${_res}" PARENT_SCOPE) -endfunction() - -function(ConvertFromHex hex dec) - string(TOLOWER ${hex} hex) - string(LENGTH "${hex}" _strlen) - set(_res 0) - while(_strlen GREATER 0) - math(EXPR _res "${_res} * 16") - string(SUBSTRING "${hex}" 0 1 NIBBLE) - string(SUBSTRING "${hex}" 1 -1 hex) - string(FIND ${HEX_ALPHABET} ${NIBBLE} value) - if(value EQUAL -1) - message(FATAL_ERROR "Invalid hex character '${NIBBLE}'") - endif() - math(EXPR _res "${_res} + ${value}") - string(LENGTH "${hex}" _strlen) - endwhile() - set(${dec} ${_res} PARENT_SCOPE) -endfunction() - -# Based on http://www.json.org/JSON_checker/utf8_decode.c -function(DecodeUtf8 hexBytes codePoint) - string(SUBSTRING ${hexBytes} 0 2 hexByte1) - ConvertFromHex(${hexByte1} byte1) - # Zero continuations (0 to 127) - math(EXPR out "${byte1} & 128") - if(out EQUAL 0) - set(${codePoint} ${byte1} PARENT_SCOPE) - return() - endif() - # One continuation (128 to 2047) - math(EXPR out "${byte1} & 224") - if(out EQUAL 192) - string(SUBSTRING ${hexBytes} 2 2 hexByte2) - ConvertFromHex(${hexByte2} byte2) - math(EXPR result "((${byte1} & 31) << 6) | ${byte2}") - if(result GREATER 127) - set(${codePoint} ${result} PARENT_SCOPE) - return() - endif() - else() - # Two continuations (2048 to 55295 and 57344 to 65535) - math(EXPR result "${byte1} & 240") - if(result EQUAL 224) - string(SUBSTRING ${hexBytes} 2 2 hexByte2) - string(SUBSTRING ${hexBytes} 4 2 hexByte3) - ConvertFromHex(${hexByte2} byte2) - ConvertFromHex(${hexByte3} byte3) - math(EXPR result "${byte2} | ${byte3}") - if(result GREATER -1) - math(EXPR result "((${byte1} & 15) << 12) | (${byte2} << 6) | ${byte3}") - if((result GREATER 2047) AND (result LESS 55296 OR result GREATER 57343)) - set(${codePoint} ${result} PARENT_SCOPE) - return() - endif() - endif() - else() - # Three continuations (65536 to 1114111) - math(EXPR result "${byte1} & 248") - if(result EQUAL 224) - string(SUBSTRING ${hexBytes} 2 2 hexByte2) - string(SUBSTRING ${hexBytes} 4 2 hexByte3) - string(SUBSTRING ${hexBytes} 6 2 hexByte4) - ConvertFromHex(${hexByte2} byte2) - ConvertFromHex(${hexByte3} byte3) - ConvertFromHex(${hexByte4} byte4) - math(EXPR result "${byte2} | ${byte3} | ${byte4}") - if(result GREATER -1) - math(EXPR result "((c & 7) << 18) | (c1 << 12) | (c2 << 6) | c3") - if((result GREATER 65535) AND (result LESS 1114112)) - set(${codePoint} ${result} PARENT_SCOPE) - return() - endif() - endif() - endif() - endif() - endif() - message(FATAL_ERROR "Invalid UTF-8 encoding") -endfunction() - -set(inputFile ${CMAKE_ARGV3}) -set(outputFile ${CMAKE_ARGV4}) -# Get the file contents in text and hex-encoded format because -# CMake doesn't provide functions for converting between the two -file(READ "${inputFile}" contents) -file(READ "${inputFile}" hexContents HEX) - -# Convert the text contents into a list of lines by escaping -# the list separator ';' and then replacing new line characters -# with the list separator -string(REGEX REPLACE ";" "\\\\;" contents ${contents}) -string(REGEX REPLACE "\n" ";" contents ${contents}) - -get_filename_component(encname ${inputFile} NAME_WE) -set(output - "static const struct StaticTableEncoding encoding_${encname} = {\n" - " { .decode = &decode_table },\n" - " {") -set(hexIndex 0) -foreach(line ${contents}) - string(LENGTH ${line} lineLength) - # Convert "A" to 0x41 - string(FIND ${line} "\"" beginQuote) - if(NOT ${beginQuote} EQUAL -1) - string(FIND ${line} "\"" endQuote REVERSE) - if(${beginQuote} EQUAL ${endQuote}) - message(FATAL_ERROR "Line contains only one quote") - endif() - math(EXPR beginHexQuote "${hexIndex} + (${beginQuote} + 1)*2") - math(EXPR endHexQuote "${hexIndex} + (${endQuote} + 1)*2") - math(EXPR quoteLen "${endHexQuote} - ${beginHexQuote} - 1") - string(SUBSTRING ${hexContents} ${beginHexQuote} ${quoteLen} hexQuote) - DecodeUtf8(${hexQuote} codePoint) - ConvertToHex(${codePoint} hexCodePoint 4) - STRING(REGEX REPLACE "\"(.+)\"" ${hexCodePoint} line ${line}) - endif() - # Strip comment - string(REGEX REPLACE "[ \t\n]*#.*" "" line ${line}) - # Convert 3/1 to [0x31] - string(REGEX REPLACE "^([0-9]+)/([0-9]+).*" "\\1;\\2" numbers ${line}) - list(GET numbers 0 upperBits) - list(GET numbers 1 lowerBits) - math(EXPR res "${upperBits}*16 + ${lowerBits}") - ConvertToHex(${res} hex 2) - string(REGEX REPLACE "^([0-9]+)/([0-9]+)" "[${hex}]" line ${line}) - # Convert U+0041 to 0x0041 - string(REPLACE "U+" "0x" line ${line}) - # Indent and append a comma - set(line " ${line},") - set(output "${output}\n${line}") - # Increment the index by the number of characters in the line, - # plus one for the new line character then multiple by two for the hex digit index - math(EXPR hexIndex "${hexIndex} + 2*(${lineLength} + 1)") -endforeach() -set(output "${output}\n" - " }\n" - "}\;\n") - -file(WRITE ${outputFile} ${output}) diff --git a/cmake.deps/cmake/LibvtermCMakeLists.txt b/cmake.deps/cmake/LibvtermCMakeLists.txt index 079ad28ba0..777ce6c54c 100644 --- a/cmake.deps/cmake/LibvtermCMakeLists.txt +++ b/cmake.deps/cmake/LibvtermCMakeLists.txt @@ -2,43 +2,18 @@ cmake_minimum_required(VERSION 3.10) project(libvterm LANGUAGES C) include(GNUInstallDirs) -find_package(Perl) if(MSVC) - add_compile_options(/W3) add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE) else() - add_compile_options(-Wall -std=c99) + add_compile_options(-std=c99) endif() -# Generate includes from tables -file(GLOB TBL_FILES ${CMAKE_SOURCE_DIR}/src/encoding/*.tbl) -set(TBL_FILES_HEADERS) -foreach(file ${TBL_FILES}) - get_filename_component(basename ${file} NAME_WE) - set(tname encoding/${basename}.inc) - add_custom_command(OUTPUT - COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/encoding/ - COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/tbl2inc_c.cmake ${file} ${CMAKE_BINARY_DIR}/${tname} - COMMENT "Generating ${tname}" - OUTPUT ${CMAKE_BINARY_DIR}/${tname} - ) - list(APPEND TBL_FILES_HEADERS ${tname}) - # Only used for verifying that the output of tbl2inc_c.cmake is correct - set(tname encoding-test/${basename}.inc) - add_custom_command(OUTPUT - COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/encoding-test/ - COMMAND ${PERL_EXECUTABLE} -CSD ${CMAKE_SOURCE_DIR}/tbl2inc_c.pl ${file} > ${CMAKE_BINARY_DIR}/${tname} - COMMENT "Generating ${tname}" - OUTPUT ${CMAKE_BINARY_DIR}/${tname} - ) -endforeach() - include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${CMAKE_BINARY_DIR}) file(GLOB VTERM_SOURCES ${CMAKE_SOURCE_DIR}/src/*.c) -add_library(vterm ${VTERM_SOURCES} ${TBL_FILES_HEADERS}) +add_library(vterm ${VTERM_SOURCES}) install(TARGETS vterm ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES include/vterm.h include/vterm_keycodes.h @@ -54,33 +29,4 @@ if(NOT WIN32) endforeach() endif() -# Tests -add_executable(harness EXCLUDE_FROM_ALL t/harness.c) -target_link_libraries(harness vterm) -set_target_properties(harness PROPERTIES - # run-test.pl expects to find the harness in t/.libs/ - RUNTIME_OUTPUT_DIRECTORY t/.libs) - -if(Perl_FOUND) - file(GLOB TESTFILES ${CMAKE_SOURCE_DIR}/t/*.test) - add_custom_target(check) - foreach(testfile ${TESTFILES}) - get_filename_component(target_name ${testfile} NAME_WE) - add_custom_target(${target_name} - COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/t/run-test.pl ${testfile} - COMMENT "**${target_name} **" - DEPENDS harness) - add_dependencies(check ${target_name}) - endforeach() - - foreach(header_path ${TBL_FILES_HEADERS}) - get_filename_component(header_name ${header_path} NAME) - set(perl_header_path ${CMAKE_BINARY_DIR}/encoding-test/${header_name}) - add_custom_target(test-${header_name} - COMMAND ${CMAKE_COMMAND} -E compare_files - ${header_path} ${perl_header_path} - DEPENDS ${header_path} ${perl_header_path}) - endforeach() -endif() - # vim: set ft=cmake: diff --git a/cmake/FindLIBVTERM.cmake b/cmake/FindLIBVTERM.cmake deleted file mode 100644 index 469494ddfd..0000000000 --- a/cmake/FindLIBVTERM.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# - Try to find libvterm -# Once done this will define -# LIBVTERM_FOUND - System has libvterm -# LIBVTERM_INCLUDE_DIRS - The libvterm include directories -# LIBVTERM_LIBRARIES - The libraries needed to use libvterm - -include(LibFindMacros) - -libfind_pkg_detect(LIBVTERM vterm FIND_PATH vterm.h FIND_LIBRARY vterm) -libfind_process(LIBVTERM REQUIRED) diff --git a/cmake/FindUNIBILIUM.cmake b/cmake/FindUNIBILIUM.cmake deleted file mode 100644 index 0bf27b45e2..0000000000 --- a/cmake/FindUNIBILIUM.cmake +++ /dev/null @@ -1,12 +0,0 @@ -# - Try to find unibilium -# Once done this will define -# UNIBILIUM_FOUND - System has unibilium -# UNIBILIUM_INCLUDE_DIRS - The unibilium include directories -# UNIBILIUM_LIBRARIES - The libraries needed to use unibilium - -include(LibFindMacros) - -libfind_pkg_detect(UNIBILIUM unibilium - FIND_PATH unibilium.h - FIND_LIBRARY unibilium) -libfind_process(UNIBILIUM) diff --git a/cmake/Findlibvterm.cmake b/cmake/Findlibvterm.cmake new file mode 100644 index 0000000000..d8536ca894 --- /dev/null +++ b/cmake/Findlibvterm.cmake @@ -0,0 +1,20 @@ +find_path(LIBVTERM_INCLUDE_DIR vterm.h) +find_library(LIBVTERM_LIBRARY vterm) + +if(LIBVTERM_INCLUDE_DIR AND EXISTS "${LIBVTERM_INCLUDE_DIR}/vterm.h") + file(STRINGS ${LIBVTERM_INCLUDE_DIR}/vterm.h VTERM_VERSION_MAJOR REGEX "#define VTERM_VERSION_MAJOR") + string(REGEX MATCH "[0-9]+" VTERM_VERSION_MAJOR ${VTERM_VERSION_MAJOR}) + + file(STRINGS ${LIBVTERM_INCLUDE_DIR}/vterm.h VTERM_VERSION_MINOR REGEX "#define VTERM_VERSION_MINOR") + string(REGEX MATCH "[0-9]+" VTERM_VERSION_MINOR ${VTERM_VERSION_MINOR}) + + set(VTERM_VERSION ${VTERM_VERSION_MAJOR}.${VTERM_VERSION_MINOR}) +endif() + +find_package_handle_standard_args(libvterm + REQUIRED_VARS LIBVTERM_INCLUDE_DIR LIBVTERM_LIBRARY + VERSION_VAR VTERM_VERSION) + +add_library(libvterm INTERFACE) +target_include_directories(libvterm SYSTEM BEFORE INTERFACE INTERFACE ${LIBVTERM_INCLUDE_DIR}) +target_link_libraries(libvterm INTERFACE ${LIBVTERM_LIBRARY}) diff --git a/cmake/Findunibilium.cmake b/cmake/Findunibilium.cmake new file mode 100644 index 0000000000..7bfbcba942 --- /dev/null +++ b/cmake/Findunibilium.cmake @@ -0,0 +1,27 @@ +find_path(UNIBILIUM_INCLUDE_DIR unibilium.h) +find_library(UNIBILIUM_LIBRARY unibilium) + +find_package_handle_standard_args(unibilium + REQUIRED_VARS UNIBILIUM_INCLUDE_DIR UNIBILIUM_LIBRARY) + +add_library(unibilium INTERFACE) +target_include_directories(unibilium SYSTEM BEFORE INTERFACE ${UNIBILIUM_INCLUDE_DIR}) +target_link_libraries(unibilium INTERFACE ${UNIBILIUM_LIBRARY}) + +list(APPEND CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIR}") +list(APPEND CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARY}") +check_c_source_compiles(" +#include <unibilium.h> + +int +main(void) +{ + unibi_str_from_var(unibi_var_from_str(\"\")); + return unibi_num_from_var(unibi_var_from_num(0)); +} +" UNIBI_HAS_VAR_FROM) +list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIR}") +list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARY}") +if(UNIBI_HAS_VAR_FROM) + target_compile_definitions(unibilium INTERFACE NVIM_UNIBI_HAS_VAR_FROM) +endif() diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake index c3ac5f208e..d724f43a5f 100644 --- a/cmake/RunTests.cmake +++ b/cmake/RunTests.cmake @@ -71,8 +71,16 @@ if(NOT DEFINED ENV{TEST_TIMEOUT} OR "$ENV{TEST_TIMEOUT}" STREQUAL "") endif() set(ENV{SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_NAME}) # used by test/helpers.lua. + +# TODO: eventually always use NVIM_PRG as the runner +if("${TEST_TYPE}" STREQUAL "unit") + set(RUNNER_PRG ${NVIM_PRG} -ll ${WORKING_DIR}/test/busted_runner.lua) +else() + set(RUNNER_PRG ${BUSTED_PRG}) +endif() + execute_process( - COMMAND ${BUSTED_PRG} -v -o test.busted.outputHandlers.${BUSTED_OUTPUT_TYPE} + COMMAND ${RUNNER_PRG} -v -o test.busted.outputHandlers.${BUSTED_OUTPUT_TYPE} --lazy --helper=${TEST_DIR}/${TEST_TYPE}/preload.lua --lpath=${BUILD_DIR}/?.lua --lpath=${WORKING_DIR}/runtime/lua/?.lua diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 0e1cc3c28c..95a929b808 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -3281,7 +3281,7 @@ nvim_create_autocmd({event}, {*opts}) *nvim_create_autocmd()* • match: (string) expanded value of |<amatch>| • buf: (number) expanded value of |<abuf>| • file: (string) expanded value of |<afile>| - • data: (any) arbitrary data passed to + • data: (any) arbitrary data passed from |nvim_exec_autocmds()| • command (string) optional: Vim command to execute on event. diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 4d2c85b134..2fc7dce7fc 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1959,7 +1959,7 @@ exp({expr}) *exp()* Compute()->exp() expand({string} [, {nosuf} [, {list}]]) *expand()* - Expand wildcards and the following special keywords in + Expand wildcards and the following special keywords in {string}. 'wildignorecase' applies. If {list} is given and it is |TRUE|, a List will be returned. @@ -2991,6 +2991,7 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* messages |:messages| suboptions option options packadd optional package |pack-add| names + runtime |:runtime| completion scriptnames sourced script names |:scriptnames| shellcmd Shell command sign |:sign| suboptions @@ -7568,10 +7569,14 @@ sinh({expr}) *sinh()* sockconnect({mode}, {address} [, {opts}]) *sockconnect()* Connect a socket to an address. If {mode} is "pipe" then - {address} should be the path of a named pipe. If {mode} is - "tcp" then {address} should be of the form "host:port" where - the host should be an ip adderess or host name, and port the - port number. + {address} should be the path of a local domain socket (on + unix) or named pipe (on Windows). If {mode} is "tcp" then + {address} should be of the form "host:port" where the host + should be an ip adderess or host name, and port the port + number. + + For "pipe" mode, see |luv-pipe-handle|. For "tcp" mode, see + |luv-tcp-handle|. Returns a |channel| ID. Close the socket with |chanclose()|. Use |chansend()| to send data over a bytes socket, and diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 1bbd20702b..bf77aacdc0 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -211,9 +211,7 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. When [!] is included, all found files are sourced. Else only the first found file is sourced. - When [where] is omitted, first 'runtimepath' is - searched, then directories under "start" in 'packpath' - are searched. + When [where] is omitted only 'runtimepath' is used. Other values: START search only under "start" in 'packpath' OPT search only under "opt" in 'packpath' diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 179bacdb24..5e0718c3bb 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -239,6 +239,14 @@ argument. Disables |shada| unless |-i| was given. Disables swapfile (like |-n|). + *-ll* +-ll {script} [args] + Execute a lua script, similarly to |-l|, but the editor is not + initialized. This gives a lua enviroment similar to a worker + thread. See |lua-loop-threading|. + + Unlike `-l` no prior arguments are allowed. + *-b* -b Binary mode. File I/O will only recognize <NL> to separate lines. The 'expandtab' option will be reset. The 'textwidth' diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 917863eef8..9bfdc0b94e 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -684,6 +684,7 @@ require_language({lang}, {path}, {silent}, {symbol_name}) Parameters: ~ • {lang} (string) Language the parser should parse + (alphanumerical and `_` only) • {path} (string|nil) Optional path the parser is located at • {silent} (boolean|nil) Don't throw an error if language not found diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua index 732a4ab92e..0956022ac6 100644 --- a/runtime/lua/man.lua +++ b/runtime/lua/man.lua @@ -149,15 +149,21 @@ local function highlight_line(line, linenr) if overstrike then local last_hl = hls[#hls] if char == prev_char then - if char == '_' and attr == UNDERLINE and last_hl and last_hl.final == byte then - -- This underscore is in the middle of an underlined word - attr = UNDERLINE + if char == '_' and attr == ITALIC and last_hl and last_hl.final == byte then + -- This underscore is in the middle of an italic word + attr = ITALIC else attr = BOLD end elseif prev_char == '_' then - -- char is underlined - attr = UNDERLINE + -- Even though underline is strictly what this should be. <bs>_ was used by nroff to + -- indicate italics which wasn't possible on old typewriters so underline was used. Modern + -- terminals now support italics so lets use that now. + -- See: + -- - https://unix.stackexchange.com/questions/274658/purpose-of-ascii-text-with-overstriking-file-format/274795#274795 + -- - https://cmd.inp.nsk.su/old/cmd2/manuals/unix/UNIX_Unleashed/ch08.htm + -- attr = UNDERLINE + attr = ITALIC elseif prev_char == '+' and char == 'o' then -- bullet (overstrike text '+^Ho') attr = BOLD diff --git a/runtime/lua/nvim/health.lua b/runtime/lua/nvim/health.lua index b76106f241..f11899434e 100644 --- a/runtime/lua/nvim/health.lua +++ b/runtime/lua/nvim/health.lua @@ -156,13 +156,10 @@ local function check_performance() health.report_ok(buildtype) else health.report_info(buildtype) - health.report_warn( - 'Non-optimized ' .. (has('debug') and '(DEBUG) ' or '') .. 'build. Nvim will be slower.', - { - 'Install a different Nvim package, or rebuild with `CMAKE_BUILD_TYPE=RelWithDebInfo`.', - suggest_faq, - } - ) + health.report_warn('Non-optimized debug build. Nvim will be slower.', { + 'Install a different Nvim package, or rebuild with `CMAKE_BUILD_TYPE=RelWithDebInfo`.', + suggest_faq, + }) end -- check for slow shell invocation diff --git a/runtime/lua/vim/_init_packages.lua b/runtime/lua/vim/_init_packages.lua index 0c4ee8636d..e3a442af5e 100644 --- a/runtime/lua/vim/_init_packages.lua +++ b/runtime/lua/vim/_init_packages.lua @@ -42,8 +42,11 @@ function vim._load_package(name) return nil end --- Insert vim._load_package after the preloader at position 2 -table.insert(package.loaders, 2, vim._load_package) +-- TODO(bfredl): dedicated state for this? +if vim.api then + -- Insert vim._load_package after the preloader at position 2 + table.insert(package.loaders, 2, vim._load_package) +end -- builtin functions which always should be available require('vim.shared') @@ -78,6 +81,6 @@ function vim.empty_dict() end -- only on main thread: functions for interacting with editor state -if not vim.is_thread() then +if vim.api and not vim.is_thread() then require('vim._editor') end diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 9293c828b8..8144731b09 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -226,6 +226,7 @@ local extension = { hook = function(path, bufnr) return M.getlines(bufnr, 1) == '[Trigger]' and 'conf' end, + nmconnection = 'confini', mklx = 'context', mkiv = 'context', mkii = 'context', @@ -331,6 +332,7 @@ local extension = { am = 'elf', exs = 'elixir', elm = 'elm', + lc = 'elsa', elv = 'elvish', ent = function(path, bufnr) return require('vim.filetype.detect').ent(bufnr) @@ -383,6 +385,7 @@ local extension = { ['m4gl'] = 'fgl', ['4gl'] = 'fgl', ['4gh'] = 'fgl', + fir = 'firrtl', fish = 'fish', focexec = 'focexec', fex = 'focexec', @@ -567,6 +570,7 @@ local extension = { libsonnet = 'jsonnet', jsp = 'jsp', jl = 'julia', + kdl = 'kdl', kv = 'kivy', kix = 'kix', kts = 'kotlin', @@ -1461,6 +1465,7 @@ local filename = { ['Pipfile.lock'] = 'json', ['.firebaserc'] = 'json', ['.prettierrc'] = 'json', + ['.stylelintrc'] = 'json', ['.babelrc'] = 'jsonc', ['.eslintrc'] = 'jsonc', ['.hintrc'] = 'jsonc', @@ -1696,6 +1701,7 @@ local filename = { fglrxrc = 'xml', ['/etc/blkid.tab'] = 'xml', ['/etc/blkid.tab.old'] = 'xml', + ['.clangd'] = 'yaml', ['.clang-format'] = 'yaml', ['.clang-tidy'] = 'yaml', ['/etc/zprofile'] = 'zsh', diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index c92d63b8c4..8634e53b7b 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -6,7 +6,7 @@ local M = {} --- --- Parsers are searched in the `parser` runtime directory, or the provided {path} --- ----@param lang string Language the parser should parse +---@param lang string Language the parser should parse (alphanumerical and `_` only) ---@param path (string|nil) Optional path the parser is located at ---@param silent (boolean|nil) Don't throw an error if language not found ---@param symbol_name (string|nil) Internal symbol name for the language to load @@ -16,13 +16,19 @@ function M.require_language(lang, path, silent, symbol_name) return true end if path == nil then - local fname = 'parser/' .. vim.fn.fnameescape(lang) .. '.*' + if not (lang and lang:match('[%w_]+') == lang) then + if silent then + return false + end + error("'" .. lang .. "' is not a valid language name") + end + + local fname = 'parser/' .. lang .. '.*' local paths = a.nvim_get_runtime_file(fname, false) if #paths == 0 then if silent then return false end - error("no parser for '" .. lang .. "' language, see :help treesitter-parsers") end path = paths[1] diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index 7411a7190a..7d2ea49df4 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,9 +26,12 @@ </screenshots> <releases> + <release date="2023-02-02" version="0.8.3"/> <release date="2022-12-29" version="0.8.2"/> <release date="2022-11-14" version="0.8.1"/> <release date="2022-09-30" version="0.8.0"/> + <release date="2022-06-26" version="0.7.2"/> + <release date="2022-06-26" version="0.7.1"/> <release date="2022-04-15" version="0.7.0"/> <release date="2021-12-31" version="0.6.1"/> <release date="2021-11-30" version="0.6.0"/> diff --git a/scripts/bump_deps.lua b/scripts/bump_deps.lua index 1873c3cd0d..f980e800cf 100755 --- a/scripts/bump_deps.lua +++ b/scripts/bump_deps.lua @@ -54,7 +54,7 @@ local function run_die(cmd, err_msg) end local function require_executable(cmd) - local cmd_path = run_die({ 'command', '-v', cmd }, cmd .. ' not found!') + local cmd_path = run_die({ 'sh', '-c', 'command -v ' .. cmd }, cmd .. ' not found!') run_die({ 'test', '-x', cmd_path }, cmd .. ' is not executable') end diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 2361210e59..7b56af59da 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -22,17 +22,15 @@ find_package(TreeSitter REQUIRED) target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${TreeSitter_INCLUDE_DIRS}) target_link_libraries(main_lib INTERFACE ${TreeSitter_LIBRARIES}) -find_package(UNIBILIUM 2.0 REQUIRED) -target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${UNIBILIUM_INCLUDE_DIRS}) -target_link_libraries(main_lib INTERFACE ${UNIBILIUM_LIBRARIES}) +find_package(unibilium 2.0 REQUIRED) +target_link_libraries(main_lib INTERFACE unibilium) find_package(LibTermkey 0.22 REQUIRED) target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBTERMKEY_INCLUDE_DIRS}) target_link_libraries(main_lib INTERFACE ${LIBTERMKEY_LIBRARIES}) -find_package(LIBVTERM 0.3 REQUIRED) -target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBVTERM_INCLUDE_DIRS}) -target_link_libraries(main_lib INTERFACE ${LIBVTERM_LIBRARIES}) +find_package(libvterm 0.3 REQUIRED) +target_link_libraries(main_lib INTERFACE libvterm) find_package(Iconv REQUIRED) target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${Iconv_INCLUDE_DIRS}) @@ -54,7 +52,7 @@ if(PREFER_LUA) find_package(Lua 5.1 EXACT REQUIRED) target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LUA_INCLUDE_DIR}) target_link_libraries(main_lib INTERFACE ${LUA_LIBRARIES}) - # Passive (not REQUIRED): if LUAJIT_FOUND is not set, nvim-test is skipped. + # Passive (not REQUIRED): if LUAJIT_FOUND is not set, fixtures for unittests is skipped. find_package(LuaJit) else() find_package(LuaJit REQUIRED) @@ -175,24 +173,6 @@ if(CI_BUILD) endif() endif() -list(APPEND CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIRS}") -list(APPEND CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARIES}") -check_c_source_compiles(" -#include <unibilium.h> - -int -main(void) -{ - unibi_str_from_var(unibi_var_from_str(\"\")); - return unibi_num_from_var(unibi_var_from_num(0)); -} -" UNIBI_HAS_VAR_FROM) -list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIRS}") -list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARIES}") -if(UNIBI_HAS_VAR_FROM) - target_compile_definitions(main_lib INTERFACE NVIM_UNIBI_HAS_VAR_FROM) -endif() - list(APPEND CMAKE_REQUIRED_INCLUDES "${MSGPACK_INCLUDE_DIRS}") check_c_source_compiles(" #include <msgpack.h> @@ -699,6 +679,14 @@ if(UNIX) endif() endif() +if(NOT LUAJIT_FOUND) + message(STATUS "luajit not found, skipping unit tests") +elseif(CMAKE_BUILD_TYPE MATCHES Debug) + glob_wrapper(UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) + list(APPEND NVIM_SOURCES ${UNIT_TEST_FIXTURES}) + target_compile_definitions(main_lib INTERFACE UNIT_TESTING) +endif() + target_sources(nvim PRIVATE ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS} ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS}) @@ -857,27 +845,6 @@ set_target_properties( target_compile_definitions(libnvim PRIVATE MAKE_LIB) target_link_libraries(libnvim PRIVATE main_lib PUBLIC libuv_lib) -if(NOT LUAJIT_FOUND) - message(STATUS "luajit not found, skipping nvim-test (unit tests) target") -else() - glob_wrapper(UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) - add_library( - nvim-test - MODULE - EXCLUDE_FROM_ALL - ${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES} - ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} - ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS} - ${UNIT_TEST_FIXTURES} - ) - target_link_libraries(nvim-test PRIVATE ${LUAJIT_LIBRARIES} main_lib PUBLIC libuv_lib) - if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - target_link_libraries(nvim-test PRIVATE "-framework CoreServices") - endif() - target_include_directories(nvim-test PRIVATE ${LUAJIT_INCLUDE_DIRS}) - target_compile_definitions(nvim-test PRIVATE UNIT_TESTING) -endif() - if(CLANG_ASAN_UBSAN) message(STATUS "Enabling Clang address sanitizer and undefined behavior sanitizer for nvim.") if(CI_BUILD) diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 931363e199..a2cb297b15 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -404,7 +404,7 @@ cleanup: /// - match: (string) expanded value of |<amatch>| /// - buf: (number) expanded value of |<abuf>| /// - file: (string) expanded value of |<afile>| -/// - data: (any) arbitrary data passed to |nvim_exec_autocmds()| +/// - data: (any) arbitrary data passed from |nvim_exec_autocmds()| /// - command (string) optional: Vim command to execute on event. Cannot be used with /// {callback} /// - once (boolean) optional: defaults to false. Run the autocommand diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index a75ee3bbd5..fb0062e524 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -110,6 +110,7 @@ return { 'TextChangedP', -- text was modified in Insert mode(popup) 'TextChangedT', -- text was modified in Terminal mode 'TextYankPost', -- after a yank or delete was done (y, d, c) + 'TextPutPost', -- after a put was done (p, P) 'UIEnter', -- after UI attaches 'UILeave', -- after UI detaches 'User', -- user defined autocommand diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 5c1e3af015..28c3374c0c 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1410,6 +1410,7 @@ struct window_S { int w_prev_fraction_row; linenr_T w_nrwidth_line_count; // line count when ml_nrwidth_width was computed. + linenr_T w_statuscol_line_count; // line count when 'statuscolumn' width was computed. int w_nrwidth_width; // nr of chars to print line count. qf_info_T *w_llist; // Location list for this window diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index be815151ef..5e4b49db24 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -109,6 +109,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp) && xp->xp_context != EXPAND_OLD_SETTING && xp->xp_context != EXPAND_OWNSYNTAX && xp->xp_context != EXPAND_PACKADD + && xp->xp_context != EXPAND_RUNTIME && xp->xp_context != EXPAND_SHELLCMD && xp->xp_context != EXPAND_TAGS && xp->xp_context != EXPAND_TAGS_LISTFILES @@ -1215,6 +1216,7 @@ char *addstar(char *fname, size_t len, int context) || context == EXPAND_OWNSYNTAX || context == EXPAND_FILETYPE || context == EXPAND_PACKADD + || context == EXPAND_RUNTIME || ((context == EXPAND_TAGS_LISTFILES || context == EXPAND_TAGS) && fname[0] == '/')) { retval = xstrnsave(fname, len); @@ -2092,6 +2094,10 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa xp->xp_pattern = (char *)arg; break; + case CMD_runtime: + set_context_in_runtime_cmd(xp, arg); + break; + #ifdef HAVE_WORKING_LIBINTL case CMD_language: return set_context_in_lang_cmd(xp, arg); @@ -2733,6 +2739,9 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM if (xp->xp_context == EXPAND_PACKADD) { return ExpandPackAddDir(pat, numMatches, matches); } + if (xp->xp_context == EXPAND_RUNTIME) { + return expand_runtime_cmd(pat, numMatches, matches); + } // When expanding a function name starting with s:, match the <SNR>nr_ // prefix. @@ -3201,11 +3210,12 @@ static int ExpandUserLua(expand_T *xp, int *num_file, char ***file) /// Expand `file` for all comma-separated directories in `path`. /// Adds matches to `ga`. -void globpath(char *path, char *file, garray_T *ga, int expand_options) +/// If "dirs" is true only expand directory names. +void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dirs) { expand_T xpc; ExpandInit(&xpc); - xpc.xp_context = EXPAND_FILES; + xpc.xp_context = dirs ? EXPAND_DIRECTORIES : EXPAND_FILES; char *buf = xmalloc(MAXPATHL); @@ -3524,11 +3534,14 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) set_context_in_menu_cmd(&xpc, "menu", xpc.xp_pattern, false); xpc.xp_pattern_len = strlen(xpc.xp_pattern); } - if (xpc.xp_context == EXPAND_SIGN) { set_context_in_sign_cmd(&xpc, xpc.xp_pattern); xpc.xp_pattern_len = strlen(xpc.xp_pattern); } + if (xpc.xp_context == EXPAND_RUNTIME) { + set_context_in_runtime_cmd(&xpc, xpc.xp_pattern); + xpc.xp_pattern_len = strlen(xpc.xp_pattern); + } theend: if (cmdline_fuzzy_completion_supported(&xpc)) { diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 031ad39f75..83aa68194c 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -412,7 +412,6 @@ static void get_statuscol_str(win_T *wp, linenr_T lnum, int row, int startrow, i bool use_cul = use_cursor_line_sign(wp, lnum); int virtnum = row - startrow - filler_lines; - set_vim_var_nr(VV_VIRTNUM, virtnum); // When called the first time for line "lnum" set num_attr if (stcp->num_attr == 0) { stcp->num_attr = sign_num_attr ? sign_num_attr @@ -437,6 +436,18 @@ static void get_statuscol_str(win_T *wp, linenr_T lnum, int row, int startrow, i } stcp->sign_text[i] = NULL; + // When a buffer's line count has changed, make a best estimate for the full + // width of the status column by building with "w_nrwidth_line_count". Add + // potentially truncated width and rebuild before drawing anything. + if (wp->w_statuscol_line_count != wp->w_nrwidth_line_count) { + wp->w_statuscol_line_count = wp->w_nrwidth_line_count; + set_vim_var_nr(VV_VIRTNUM, 0); + build_statuscol_str(wp, wp->w_nrwidth_line_count, 0, stcp->width, + ' ', stcp->text, &stcp->hlrec, stcp); + stcp->width += stcp->truncate; + } + set_vim_var_nr(VV_VIRTNUM, virtnum); + int width = build_statuscol_str(wp, lnum, relnum, stcp->width, ' ', stcp->text, &stcp->hlrec, stcp); // Force a redraw in case of error or when truncated diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 095d73f53f..96df6a3044 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1483,7 +1483,7 @@ static void init_prompt(int cmdchar_todo) } curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; coladvance(MAXCOL); - changed_bytes(curbuf->b_ml.ml_line_count, 0); + inserted_bytes(curbuf->b_ml.ml_line_count, 0, 0, (colnr_T)strlen(prompt)); } // Insert always starts after the prompt, allow editing text after it. diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index c17a44b990..9a5ab51c71 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -26,7 +26,7 @@ return { acos={args=1, base=1, float_func="acos"}, -- WJMc add={args=2, base=1}, ['and']={args=2, base=1}, - api_info={}, + api_info={fast=true}, append={args=2, base=2}, appendbufline={args=3, base=3}, argc={args={0, 1}}, @@ -64,14 +64,14 @@ return { bufwinid={args=1, base=1}, bufwinnr={args=1, base=1}, byte2line={args=1, base=1}, - byteidx={args=2, base=1}, - byteidxcomp={args=2, base=1}, + byteidx={args=2, base=1, fast=true}, + byteidxcomp={args=2, base=1, fast=true}, call={args={2, 3}, base=1}, ceil={args=1, base=1, float_func="ceil"}, changenr={}, chanclose={args={1, 2}}, chansend={args=2}, - char2nr={args={1, 2}, base=1}, + char2nr={args={1, 2}, base=1, fast=true}, charclass={args=1, base=1}, charcol={args={1, 2}, base=1}, charidx={args={2, 3}, base=1}, @@ -100,7 +100,7 @@ return { deletebufline={args={2,3}, base=1}, dictwatcheradd={args=3}, dictwatcherdel={args=3}, - did_filetype={}, + did_filetype={fast=true}, diff_filler={args=1, base=1}, diff_hlID={args=2, base=1}, digraph_get={args=1, base=1}, @@ -108,11 +108,11 @@ return { digraph_set={args=2, base=1}, digraph_setlist={args=1, base=1}, empty={args=1, base=1}, - environ={}, - escape={args=2, base=1}, + environ={fast=true}, + escape={args=2, base=1, fast=true}, eval={args=1, base=1}, eventhandler={}, - executable={args=1, base=1}, + executable={args=1, base=1, fast=true}, execute={args={1, 2}, base=1}, exepath={args=1, base=1}, exists={args=1, base=1}, @@ -122,8 +122,8 @@ return { extend={args={2, 3}, base=1}, feedkeys={args={1, 2}, base=1}, file_readable={args=1, base=1, func='f_filereadable'}, -- obsolete - filereadable={args=1, base=1}, - filewritable={args=1, base=1}, + filereadable={args=1, base=1, fast=true}, + filewritable={args=1, base=1, fast=true}, filter={args=2, base=1}, finddir={args={1, 3}, base=1}, findfile={args={1, 3}, base=1}, @@ -131,8 +131,8 @@ return { float2nr={args=1, base=1}, floor={args=1, base=1, float_func="floor"}, fmod={args=2, base=1}, - fnameescape={args=1, base=1}, - fnamemodify={args=2, base=1}, + fnameescape={args=1, base=1, fast=true}, + fnamemodify={args=2, base=1, fast=true}, foldclosed={args=1, base=1}, foldclosedend={args=1, base=1}, foldlevel={args=1, base=1}, @@ -167,17 +167,17 @@ return { getcwd={args={0, 2}, base=1}, getenv={args=1, base=1}, getfontname={args={0, 1}}, - getfperm={args=1, base=1}, - getfsize={args=1, base=1}, - getftime={args=1, base=1}, - getftype={args=1, base=1}, + getfperm={args=1, base=1, fast=true}, + getfsize={args=1, base=1, fast=true}, + getftime={args=1, base=1, fast=true}, + getftype={args=1, base=1, fast=true}, getjumplist={args={0, 2}, base=1}, getline={args={1, 2}, base=1}, getloclist={args={1, 2}}, getmarklist={args={0, 1}, base=1}, getmatches={args={0, 1}}, getmousepos={}, - getpid={}, + getpid={fast=true}, getpos={args=1, base=1}, getqflist={args={0, 1}}, getreg={args={0, 3}, base=1}, @@ -208,7 +208,7 @@ return { histnr={args=1, base=1}, hlID={args=1, base=1}, hlexists={args=1, base=1}, - hostname={}, + hostname={fast=true}, iconv={args=3, base=1, fast=true}, indent={args=1, base=1}, index={args={2, 4}, base=1}, @@ -221,7 +221,7 @@ return { insert={args={2, 3}, base=1}, interrupt={args=0}, invert={args=1, base=1}, - isdirectory={args=1, base=1}, + isdirectory={args=1, base=1, fast=true}, isinf={args=1, base=1}, islocked={args=1, base=1}, isnan={args=1, base=1}, @@ -300,13 +300,13 @@ return { reg_executing={}, reg_recording={}, reg_recorded={}, - reltime={args={0, 2}, base=1}, - reltimefloat={args=1, base=1}, - reltimestr={args=1, base=1}, + reltime={args={0, 2}, base=1, fast=true}, + reltimefloat={args=1, base=1, fast=true}, + reltimestr={args=1, base=1, fast=true}, remove={args={2, 3}, base=1}, rename={args=2, base=1}, - ['repeat']={args=2, base=1}, - resolve={args=1, base=1}, + ['repeat']={args=2, base=1, fast=true}, + resolve={args=1, base=1, fast=true}, reverse={args=1, base=1}, round={args=1, base=1, float_func="round"}, rpcnotify={args=varargs(2)}, @@ -374,24 +374,24 @@ return { split={args={1, 3}, base=1}, sqrt={args=1, base=1, float_func="sqrt"}, srand={args={0, 1}, base=1}, - stdpath={args=1}, + stdpath={args=1, fast=true}, str2float={args=1, base=1}, str2list={args={1, 2}, base=1}, str2nr={args={1, 3}, base=1}, strcharlen={args=1, base=1}, - strcharpart={args={2, 3}, base=1}, + strcharpart={args={2, 3}, base=1, fast=true}, strchars={args={1, 2}, base=1}, strdisplaywidth={args={1, 2}, base=1}, strftime={args={1, 2}, base=1}, strgetchar={args=2, base=1}, - stridx={args={2, 3}, base=1}, + stridx={args={2, 3}, base=1, fast=true}, string={args=1, base=1}, strlen={args=1, base=1}, - strpart={args={2, 4}, base=1}, + strpart={args={2, 4}, base=1, fast=true}, strptime={args=2, base=1}, strridx={args={2, 3}, base=1}, - strtrans={args=1, base=1}, - strwidth={args=1, base=1}, + strtrans={args=1, base=1, fast=true}, + strwidth={args=1, base=1, fast=true}, submatch={args={1, 2}, base=1}, substitute={args=4, base=1}, swapinfo={args=1, base=1}, @@ -419,12 +419,12 @@ return { timer_start={args={2, 3}, base=1}, timer_stop={args=1, base=1}, timer_stopall={args=0}, - tolower={args=1, base=1}, - toupper={args=1, base=1}, + tolower={args=1, base=1, fast=true}, + toupper={args=1, base=1, fast=true}, tr={args=3, base=1}, trim={args={1, 3}, base=1}, trunc={args=1, base=1, float_func="trunc"}, - type={args=1, base=1}, + type={args=1, base=1, fast=true}, undofile={args=1, base=1}, undotree={}, uniq={args={1, 3}, base=1}, @@ -447,7 +447,7 @@ return { win_splitmove={args={2, 3}, base=1}, winbufnr={args=1, base=1}, wincol={}, - windowsversion={}, + windowsversion={fast=true}, winheight={args=1, base=1}, winlayout={args={0, 1}, base=1}, winline={}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 33f8af451f..569c1462d1 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2983,7 +2983,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (file != NULL && !error) { garray_T ga; ga_init(&ga, (int)sizeof(char *), 10); - globpath((char *)tv_get_string(&argvars[0]), (char *)file, &ga, flags); + globpath((char *)tv_get_string(&argvars[0]), (char *)file, &ga, flags, false); if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); @@ -3064,9 +3064,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "conceal", "cursorbind", "cursorshape", -#ifdef DEBUG - "debug", -#endif "dialog_con", "diff", "digraphs", @@ -7826,34 +7823,35 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) // MSVC returns NULL for an invalid value of seconds. if (curtime_ptr == NULL) { rettv->vval.v_string = xstrdup(_("(Invalid)")); - } else { - vimconv_T conv; + return; + } - conv.vc_type = CONV_NONE; - char *enc = enc_locale(); - convert_setup(&conv, p_enc, enc); - if (conv.vc_type != CONV_NONE) { - p = string_convert(&conv, p, NULL); - } - char result_buf[256]; - if (p == NULL || strftime(result_buf, sizeof(result_buf), p, curtime_ptr) == 0) { - result_buf[0] = NUL; - } + vimconv_T conv; - 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 = xstrdup(result_buf); - } + conv.vc_type = CONV_NONE; + char *enc = enc_locale(); + convert_setup(&conv, p_enc, enc); + if (conv.vc_type != CONV_NONE) { + p = string_convert(&conv, p, NULL); + } + char result_buf[256]; + if (p == NULL || strftime(result_buf, sizeof(result_buf), p, curtime_ptr) == 0) { + result_buf[0] = NUL; + } - // Release conversion descriptors. - convert_setup(&conv, NULL, NULL); - xfree(enc); + 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 = xstrdup(result_buf); + } + + // Release conversion descriptors. + convert_setup(&conv, NULL, NULL); + xfree(enc); } /// "strgetchar()" function @@ -8657,6 +8655,7 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, EvalFuncData fptr emsg(_(e_number_exp)); return; } + int paused = (bool)tv_get_number(&argvars[1]); timer_T *timer = find_timer_by_nr(tv_get_number(&argvars[0])); if (timer != NULL) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 05b4737206..c298064d86 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -3208,17 +3208,19 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, dict_T **const dic /// @param[in,out] tv Value to free. void tv_clear(typval_T *const tv) { - if (tv != NULL && tv->v_type != VAR_UNKNOWN) { - // WARNING: do not translate the string here, gettext is slow and function - // is used *very* often. At the current state encode_vim_to_nothing() does - // not error out and does not use the argument anywhere. - // - // If situation changes and this argument will be used, translate it in the - // place where it is used. - const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear() argument"); - (void)evn_ret; - assert(evn_ret == OK); + if (tv == NULL || tv->v_type == VAR_UNKNOWN) { + return; } + + // WARNING: do not translate the string here, gettext is slow and function + // is used *very* often. At the current state encode_vim_to_nothing() does + // not error out and does not use the argument anywhere. + // + // If situation changes and this argument will be used, translate it in the + // place where it is used. + const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear() argument"); + (void)evn_ret; + assert(evn_ret == OK); } //{{{3 Free @@ -3228,35 +3230,37 @@ void tv_clear(typval_T *const tv) /// @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_BLOB: - tv_blob_unref(tv->vval.v_blob); - 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_BOOL: - case VAR_SPECIAL: - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; - } - xfree(tv); + if (tv == NULL) { + return; + } + + 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_BLOB: + tv_blob_unref(tv->vval.v_blob); + 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_BOOL: + case VAR_SPECIAL: + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_UNKNOWN: + break; } + xfree(tv); } //{{{3 Copy diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 3fe5a0d7c7..ab81c190b0 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -760,13 +760,12 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force) static bool func_remove(ufunc_T *fp) { hashitem_T *hi = hash_find(&func_hashtab, (char *)UF2HIKEY(fp)); - - if (!HASHITEM_EMPTY(hi)) { - hash_remove(&func_hashtab, hi); - return true; + if (HASHITEM_EMPTY(hi)) { + return false; } - return false; + hash_remove(&func_hashtab, hi); + return true; } static void func_clear_items(ufunc_T *fp) diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 58fb2211fc..f38dc90e9d 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1352,7 +1352,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, // Make sure dict is valid assert(dict != NULL); - v = xmalloc(sizeof(dictitem_T) + strlen(varname)); + v = xmalloc(offsetof(dictitem_T, di_key) + strlen(varname) + 1); STRCPY(v->di_key, varname); if (hash_add(ht, (char *)v->di_key) == FAIL) { xfree(v); diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index e236f23895..a0435afd65 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1092,7 +1092,7 @@ static int ff_check_visited(ff_visited_T **visited_list, char *fname, char *wc_p } // New file/dir. Add it to the list of visited files/dirs. - vp = xmalloc(sizeof(ff_visited_T) + strlen(ff_expand_buffer)); + vp = xmalloc(offsetof(ff_visited_T, ffv_fname) + strlen(ff_expand_buffer) + 1); if (!url) { vp->file_id_valid = true; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 9da9d6199e..fbb5c4f1fa 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -968,13 +968,11 @@ retry: if (read_buf_lnum > from) { size = 0; } else { - int n, ni; - long tlen; - - tlen = 0; + int ni; + long tlen = 0; for (;;) { p = (char_u *)ml_get(read_buf_lnum) + read_buf_col; - n = (int)strlen((char *)p); + int n = (int)strlen((char *)p); if ((int)tlen + n + 1 > size) { // Filled up to "size", append partial line. // Change NL to NUL to reverse the effect done @@ -2126,7 +2124,6 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en bool errmsg_allocated = false; char *buffer; char smallbuf[SMBUFSIZE]; - char *backup_ext; int bufsize; long perm; // file permissions int retval = OK; @@ -2555,8 +2552,6 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en if ((bkc & BKC_YES) || append) { // "yes" backup_copy = true; } else if ((bkc & BKC_AUTO)) { // "auto" - int i; - // Don't rename the file when: // - it's a hard link // - it's a symbolic link @@ -2571,7 +2566,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en // First find a file name that doesn't exist yet (use some // arbitrary numbers). STRCPY(IObuff, fname); - for (i = 4913;; i += 123) { + for (int i = 4913;; i += 123) { char *tail = path_tail(IObuff); size_t size = (size_t)(tail - IObuff); snprintf(tail, IOSIZE - size, "%d", i); @@ -2624,18 +2619,10 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en } // make sure we have a valid backup extension to use - if (*p_bex == NUL) { - backup_ext = ".bak"; - } else { - backup_ext = p_bex; - } + char *backup_ext = *p_bex == NUL ? ".bak" : p_bex; if (backup_copy) { - char *wp; int some_error = false; - char *dirp; - char *rootname; - char *p; // Try to make the backup in each directory in the 'bdir' option. // @@ -2647,11 +2634,11 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en // // For these reasons, the existing writable file must be truncated // and reused. Creation of a backup COPY will be attempted. - dirp = p_bdir; + char *dirp = p_bdir; while (*dirp) { // Isolate one directory name, using an entry in 'bdir'. size_t dir_len = copy_option_part(&dirp, IObuff, IOSIZE, ","); - p = IObuff + dir_len; + char *p = IObuff + dir_len; bool trailing_pathseps = after_pathsep(IObuff, p) && p[-1] == p[-2]; if (trailing_pathseps) { IObuff[dir_len - 2] = NUL; @@ -2674,7 +2661,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en } } - rootname = get_file_in_dir(fname, IObuff); + char *rootname = get_file_in_dir(fname, IObuff); if (rootname == NULL) { some_error = true; // out of memory goto nobackup; @@ -2710,7 +2697,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en // delete an existing one, and try to use another name instead. // Change one character, just before the extension. // - wp = backup + strlen(backup) - 1 - strlen(backup_ext); + char *wp = backup + strlen(backup) - 1 - strlen(backup_ext); if (wp < backup) { // empty file name ??? wp = backup; } @@ -2781,10 +2768,6 @@ nobackup: } SET_ERRMSG(NULL); } else { - char *dirp; - char *p; - char *rootname; - // Make a backup by renaming the original file. // If 'cpoptions' includes the "W" flag, we don't want to @@ -2798,11 +2781,11 @@ nobackup: // Form the backup file name - change path/fo.o.h to // path/fo.o.h.bak Try all directories in 'backupdir', first one // that works is used. - dirp = p_bdir; + char *dirp = p_bdir; while (*dirp) { // Isolate one directory name and make the backup file name. size_t dir_len = copy_option_part(&dirp, IObuff, IOSIZE, ","); - p = IObuff + dir_len; + char *p = IObuff + dir_len; bool trailing_pathseps = after_pathsep(IObuff, p) && p[-1] == p[-2]; if (trailing_pathseps) { IObuff[dir_len - 2] = NUL; @@ -2826,7 +2809,7 @@ nobackup: } if (backup == NULL) { - rootname = get_file_in_dir(fname, IObuff); + char *rootname = get_file_in_dir(fname, IObuff); if (rootname == NULL) { backup = NULL; } else { @@ -3690,9 +3673,7 @@ static bool msg_add_fileformat(int eol_type) /// Append line and character count to IObuff. void msg_add_lines(int insert_space, long lnum, off_T nchars) { - char *p; - - p = IObuff + strlen(IObuff); + char *p = IObuff + strlen(IObuff); if (insert_space) { *p++ = ' '; @@ -3758,46 +3739,35 @@ static bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) F /// @return FAIL for failure, OK otherwise. static int buf_write_bytes(struct bw_info *ip) { - int wlen; - char *buf = ip->bw_buf; // data to write - int len = ip->bw_len; // length of data + char *buf = ip->bw_buf; // data to write + int len = ip->bw_len; // length of data #ifdef HAS_BW_FLAGS - int flags = ip->bw_flags; // extra flags + int flags = ip->bw_flags; // extra flags #endif // Skip conversion when writing the BOM. if (!(flags & FIO_NOCONVERT)) { - char *p; - unsigned c; - int n; - if (flags & FIO_UTF8) { // Convert latin1 in the buffer to UTF-8 in the file. - p = ip->bw_conv_buf; // translate to buffer - for (wlen = 0; wlen < len; wlen++) { + char *p = ip->bw_conv_buf; // translate to buffer + for (int wlen = 0; wlen < len; wlen++) { p += utf_char2bytes((uint8_t)buf[wlen], p); } buf = ip->bw_conv_buf; len = (int)(p - ip->bw_conv_buf); } else if (flags & (FIO_UCS4 | FIO_UTF16 | FIO_UCS2 | FIO_LATIN1)) { + unsigned c; + int n = 0; // Convert UTF-8 bytes in the buffer to UCS-2, UCS-4, UTF-16 or // Latin1 chars in the file. - if (flags & FIO_LATIN1) { - p = buf; // translate in-place (can only get shorter) - } else { - p = ip->bw_conv_buf; // translate to buffer - } - for (wlen = 0; wlen < len; wlen += n) { + // translate in-place (can only get shorter) or to buffer + char *p = flags & FIO_LATIN1 ? buf : ip->bw_conv_buf; + for (int wlen = 0; wlen < len; wlen += n) { if (wlen == 0 && ip->bw_restlen != 0) { - int l; - // Use remainder of previous call. Append the start of // buf[] to get a full sequence. Might still be too // short! - l = CONV_RESTLEN - ip->bw_restlen; - if (l > len) { - l = len; - } + int l = MIN(len, CONV_RESTLEN - ip->bw_restlen); memmove(ip->bw_rest + ip->bw_restlen, buf, (size_t)l); n = utf_ptr2len_len((char *)ip->bw_rest, ip->bw_restlen + l); if (n > ip->bw_restlen + len) { @@ -3864,18 +3834,15 @@ static int buf_write_bytes(struct bw_info *ip) if (ip->bw_iconv_fd != (iconv_t)-1) { const char *from; size_t fromlen; - char *to; size_t tolen; // Convert with iconv(). if (ip->bw_restlen > 0) { - char *fp; - // Need to concatenate the remainder of the previous call and // the bytes of the current call. Use the end of the // conversion buffer for this. fromlen = (size_t)len + (size_t)ip->bw_restlen; - fp = ip->bw_conv_buf + ip->bw_conv_buflen - fromlen; + char *fp = ip->bw_conv_buf + ip->bw_conv_buflen - fromlen; memmove(fp, ip->bw_rest, (size_t)ip->bw_restlen); memmove(fp + ip->bw_restlen, buf, (size_t)len); from = fp; @@ -3885,7 +3852,7 @@ static int buf_write_bytes(struct bw_info *ip) fromlen = (size_t)len; tolen = ip->bw_conv_buflen; } - to = ip->bw_conv_buf; + char *to = ip->bw_conv_buf; if (ip->bw_first) { size_t save_len = tolen; @@ -3925,7 +3892,7 @@ static int buf_write_bytes(struct bw_info *ip) // Only checking conversion, which is OK if we get here. return OK; } - wlen = (int)write_eintr(ip->bw_fd, buf, (size_t)len); + int wlen = (int)write_eintr(ip->bw_fd, buf, (size_t)len); return (wlen < len) ? FAIL : OK; } @@ -3940,7 +3907,6 @@ static bool ucs2bytes(unsigned c, char **pp, int flags) FUNC_ATTR_NONNULL_ALL { char_u *p = (char_u *)(*pp); bool error = false; - int cc; if (flags & FIO_UCS4) { if (flags & FIO_ENDIAN_L) { @@ -3963,7 +3929,7 @@ static bool ucs2bytes(unsigned c, char **pp, int flags) FUNC_ATTR_NONNULL_ALL if (c >= 0x100000) { error = true; } - cc = (int)(((c >> 10) & 0x3ff) + 0xd800); + int cc = (int)(((c >> 10) & 0x3ff) + 0xd800); if (flags & FIO_ENDIAN_L) { *p++ = (uint8_t)cc; *p++ = (uint8_t)(cc >> 8); @@ -4006,7 +3972,6 @@ static bool need_conversion(const char *fenc) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int same_encoding; - int enc_flags; int fenc_flags; if (*fenc == NUL || strcmp(p_enc, fenc) == 0) { @@ -4015,7 +3980,7 @@ static bool need_conversion(const char *fenc) } else { // Ignore difference between "ansi" and "latin1", "ucs-4" and // "ucs-4be", etc. - enc_flags = get_fio_flags(p_enc); + int enc_flags = get_fio_flags(p_enc); fenc_flags = get_fio_flags(fenc); same_encoding = (enc_flags != 0 && fenc_flags == enc_flags); } @@ -4036,12 +4001,10 @@ static bool need_conversion(const char *fenc) /// @param name string to check for encoding static int get_fio_flags(const char *name) { - int prop; - if (*name == NUL) { name = p_enc; } - prop = enc_canon_props(name); + int prop = enc_canon_props(name); if (prop & ENC_UNICODE) { if (prop & ENC_2BYTE) { if (prop & ENC_ENDIAN_L) { @@ -4121,10 +4084,7 @@ static char *check_for_bom(const char *p_in, long size, int *lenp, int flags) /// @return the length of the BOM (zero when no BOM). static int make_bom(char_u *buf, char *name) { - int flags; - char *p; - - flags = get_fio_flags(name); + int flags = get_fio_flags(name); // Can't put a BOM in a non-Unicode file. if (flags == FIO_LATIN1 || flags == 0) { @@ -4137,7 +4097,7 @@ static int make_bom(char_u *buf, char *name) buf[2] = 0xbf; return 3; } - p = (char *)buf; + char *p = (char *)buf; (void)ucs2bytes(0xfeff, &p, flags); return (int)((char_u *)p - buf); } @@ -4153,8 +4113,6 @@ static int make_bom(char_u *buf, char *name) /// name. void shorten_buf_fname(buf_T *buf, char *dirname, int force) { - char *p; - if (buf->b_fname != NULL && !bt_nofilename(buf) && !path_with_url(buf->b_fname) @@ -4164,7 +4122,7 @@ void shorten_buf_fname(buf_T *buf, char *dirname, int force) if (buf->b_sfname != buf->b_ffname) { XFREE_CLEAR(buf->b_sfname); } - p = path_shorten_fname(buf->b_ffname, dirname); + char *p = path_shorten_fname(buf->b_ffname, dirname); if (p != NULL) { buf->b_sfname = xstrdup(p); buf->b_fname = buf->b_sfname; @@ -4461,10 +4419,7 @@ int vim_rename(const char *from, const char *to) { int fd_in; int fd_out; - int n; char *errmsg = NULL; - char *buffer; - long perm; #ifdef HAVE_ACL vim_acl_T acl; // ACL from original file #endif @@ -4506,7 +4461,7 @@ int vim_rename(const char *from, const char *to) return -1; } STRCPY(tempname, from); - for (n = 123; n < 99999; n++) { + for (int n = 123; n < 99999; n++) { char *tail = path_tail(tempname); snprintf(tail, (size_t)((MAXPATHL + 1) - (tail - tempname - 1)), "%d", n); @@ -4540,7 +4495,7 @@ int vim_rename(const char *from, const char *to) } // Rename() failed, try copying the file. - perm = os_getperm(from); + long perm = os_getperm(from); #ifdef HAVE_ACL // For systems that support ACL: get the ACL from the original file. acl = os_get_acl(from); @@ -4566,7 +4521,7 @@ int vim_rename(const char *from, const char *to) // Avoid xmalloc() here as vim_rename() is called by buf_write() when nvim // is `preserve_exit()`ing. - buffer = try_malloc(BUFSIZE); + char *buffer = try_malloc(BUFSIZE); if (buffer == NULL) { close(fd_out); close(fd_in); @@ -4576,6 +4531,7 @@ int vim_rename(const char *from, const char *to) return -1; } + int n; while ((n = (int)read_eintr(fd_in, buffer, BUFSIZE)) > 0) { if (write_eintr(fd_out, buffer, (size_t)n) != n) { errmsg = _("E208: Error writing to \"%s\""); @@ -4619,8 +4575,6 @@ static int already_warned = false; /// @return true if some message was written (screen should be redrawn and cursor positioned). int check_timestamps(int focus) { - int didit = 0; - // Don't check timestamps while system() or another low-level function may // cause us to lose and gain focus. if (no_check_timestamps > 0) { @@ -4635,6 +4589,8 @@ int check_timestamps(int focus) return false; } + int didit = 0; + if (!stuff_empty() || global_busy || !typebuf_typed() || autocmd_busy || curbuf->b_ro_locked > 0 || allbuf_lock > 0) { @@ -4678,13 +4634,11 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf) { buf_T *tbuf = curbuf; int retval = OK; - linenr_T lnum; - char *p; // Copy the lines in "frombuf" to "tobuf". curbuf = tobuf; - for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; lnum++) { - p = xstrdup(ml_get_buf(frombuf, lnum, false)); + for (linenr_T lnum = 1; lnum <= frombuf->b_ml.ml_line_count; lnum++) { + char *p = xstrdup(ml_get_buf(frombuf, lnum, false)); if (ml_append(lnum - 1, p, 0, false) == FAIL) { xfree(p); retval = FAIL; @@ -4696,7 +4650,7 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf) // Delete all the lines in "frombuf". if (retval != FAIL) { curbuf = frombuf; - for (lnum = curbuf->b_ml.ml_line_count; lnum > 0; lnum--) { + for (linenr_T lnum = curbuf->b_ml.ml_line_count; lnum > 0; lnum--) { if (ml_delete(lnum, false) == FAIL) { // Oops! We could try putting back the saved lines, but that // might fail again... @@ -4720,7 +4674,6 @@ int buf_check_timestamp(buf_T *buf) FUNC_ATTR_NONNULL_ALL { int retval = 0; - char *path; char *mesg = NULL; char *mesg2 = ""; bool helpmesg = false; @@ -4735,8 +4688,6 @@ int buf_check_timestamp(buf_T *buf) uint64_t orig_size = buf->b_orig_size; int orig_mode = buf->b_orig_mode; static bool busy = false; - char *s; - char *reason; bufref_T bufref; set_bufref(&bufref, buf); @@ -4784,6 +4735,7 @@ int buf_check_timestamp(buf_T *buf) // was set, the global option value otherwise. reload = RELOAD_NORMAL; } else { + char *reason; if (!file_info_ok) { reason = "deleted"; } else if (bufIsChanged(buf)) { @@ -4810,7 +4762,7 @@ int buf_check_timestamp(buf_T *buf) if (!bufref_valid(&bufref)) { emsg(_("E246: FileChangedShell autocommand deleted buffer")); } - s = get_vim_var_str(VV_FCS_CHOICE); + char *s = get_vim_var_str(VV_FCS_CHOICE); if (strcmp(s, "reload") == 0 && *reason != 'd') { reload = RELOAD_NORMAL; } else if (strcmp(s, "edit") == 0) { @@ -4863,7 +4815,7 @@ int buf_check_timestamp(buf_T *buf) } if (mesg != NULL) { - path = home_replace_save(buf, buf->b_fname); + char *path = home_replace_save(buf, buf->b_fname); if (!helpmesg) { mesg2 = ""; } @@ -4946,8 +4898,6 @@ int buf_check_timestamp(buf_T *buf) void buf_reload(buf_T *buf, int orig_mode, bool reload_options) { exarg_T ea; - pos_T old_cursor; - linenr_T old_topline; int old_ro = buf->b_p_ro; buf_T *savebuf; bufref_T bufref; @@ -4967,8 +4917,8 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options) prep_exarg(&ea, buf); } - old_cursor = curwin->w_cursor; - old_topline = curwin->w_topline; + pos_T old_cursor = curwin->w_cursor; + linenr_T old_topline = curwin->w_topline; if (p_ur < 0 || curbuf->b_ml.ml_line_count <= p_ur) { // Save all the text, so that the reload can be undone. @@ -5454,24 +5404,19 @@ bool match_file_pat(char *pattern, regprog_T **prog, char *fname, char *sfname, bool match_file_list(char *list, char *sfname, char *ffname) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 3) { - char buf[100]; - char *tail; - char *regpat; - char allow_dirs; - bool match; - char *p; - - tail = path_tail(sfname); + char *tail = path_tail(sfname); // try all patterns in 'wildignore' - p = list; + char *p = list; while (*p) { + char buf[100]; copy_option_part(&p, buf, ARRAY_SIZE(buf), ","); - regpat = file_pat_to_reg_pat(buf, NULL, &allow_dirs, false); + char allow_dirs; + char *regpat = file_pat_to_reg_pat(buf, NULL, &allow_dirs, false); if (regpat == NULL) { break; } - match = match_file_pat(regpat, NULL, ffname, sfname, tail, (int)allow_dirs); + bool match = match_file_pat(regpat, NULL, ffname, sfname, tail, (int)allow_dirs); xfree(regpat); if (match) { return true; @@ -5494,15 +5439,10 @@ bool match_file_list(char *list, char *sfname, char *ffname) char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs, int no_bslash) FUNC_ATTR_NONNULL_ARG(1) { - const char *endp; - char *reg_pat; - const char *p; - int nested = 0; - bool add_dollar = true; - if (allow_dirs != NULL) { *allow_dirs = false; } + if (pat_end == NULL) { pat_end = pat + strlen(pat); } @@ -5513,7 +5453,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs size_t size = 2; // '^' at start, '$' at end. - for (p = pat; p < pat_end; p++) { + for (const char *p = pat; p < pat_end; p++) { switch (*p) { case '*': case '.': @@ -5534,7 +5474,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs break; } } - reg_pat = xmalloc(size + 1); + char *reg_pat = xmalloc(size + 1); size_t i = 0; @@ -5545,14 +5485,16 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs } else { reg_pat[i++] = '^'; } - endp = pat_end - 1; + const char *endp = pat_end - 1; + bool add_dollar = true; if (endp >= pat && *endp == '*') { while (endp - pat > 0 && *endp == '*') { endp--; } add_dollar = false; } - for (p = pat; *p && nested >= 0 && p <= endp; p++) { + int nested = 0; + for (const char *p = pat; *p && nested >= 0 && p <= endp; p++) { switch (*p) { case '*': reg_pat[i++] = '.'; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 9c5364e1b1..8ed9381bca 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -252,7 +252,7 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle } else { len = (size_t)slen; } - buffblock_T *p = xmalloc(sizeof(buffblock_T) + len); + buffblock_T *p = xmalloc(offsetof(buffblock_T, b_str) + len + 1); buf->bh_space = len - (size_t)slen; xstrlcpy(p->b_str, s, (size_t)slen + 1); @@ -2526,10 +2526,12 @@ static int vgetorpeek(bool advance) // move cursor left, if possible if (curwin->w_cursor.col != 0) { if (curwin->w_wcol > 0) { - if (did_ai) { - // We are expecting to truncate the trailing - // white-space, so find the last non-white - // character -- webb + // After auto-indenting and no text is following, + // we are expecting to truncate the trailing + // white-space, so find the last non-white + // character -- webb + if (did_ai + && *skipwhite(get_cursor_line_ptr() + curwin->w_cursor.col) == NUL) { curwin->w_wcol = 0; ptr = (char_u *)get_cursor_line_ptr(); chartabsize_T cts; diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 9dab91cc2b..c20eac3c28 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -842,14 +842,14 @@ void hlattrs2dict(Dictionary *dict, HlAttrs ae, bool use_rgb) PUT_C(hl, "underline", BOOLEAN_OBJ(true)); break; - case HL_UNDERDOUBLE: - PUT_C(hl, "underdouble", BOOLEAN_OBJ(true)); - break; - case HL_UNDERCURL: PUT_C(hl, "undercurl", BOOLEAN_OBJ(true)); break; + case HL_UNDERDOUBLE: + PUT_C(hl, "underdouble", BOOLEAN_OBJ(true)); + break; + case HL_UNDERDOTTED: PUT_C(hl, "underdotted", BOOLEAN_OBJ(true)); break; @@ -930,8 +930,8 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e CHECK_FLAG(dict, mask, bold, , HL_BOLD); CHECK_FLAG(dict, mask, italic, , HL_ITALIC); CHECK_FLAG(dict, mask, underline, , HL_UNDERLINE); - CHECK_FLAG(dict, mask, underdouble, , HL_UNDERDOUBLE); CHECK_FLAG(dict, mask, undercurl, , HL_UNDERCURL); + CHECK_FLAG(dict, mask, underdouble, , HL_UNDERDOUBLE); CHECK_FLAG(dict, mask, underdotted, , HL_UNDERDOTTED); CHECK_FLAG(dict, mask, underdashed, , HL_UNDERDASHED); CHECK_FLAG(dict, mask, standout, , HL_STANDOUT); diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index a4dcf6eb60..95c81ac9db 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -18,8 +18,8 @@ typedef enum { // The next three bits are all underline styles HL_UNDERLINE_MASK = 0x38, HL_UNDERLINE = 0x08, - HL_UNDERDOUBLE = 0x10, - HL_UNDERCURL = 0x18, + HL_UNDERCURL = 0x10, + HL_UNDERDOUBLE = 0x18, HL_UNDERDOTTED = 0x20, HL_UNDERDASHED = 0x28, // 0x30 and 0x38 spare for underline styles diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 5b1ea9967d..3d91335f55 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -1553,12 +1553,17 @@ static bool highlight_list_arg(const int id, bool didh, const int type, int iarg } else { // type == LIST_ATTR buf[0] = NUL; for (int i = 0; hl_attr_table[i] != 0; i++) { - if (iarg & hl_attr_table[i]) { + if (((hl_attr_table[i] & HL_UNDERLINE_MASK) + && ((iarg & HL_UNDERLINE_MASK) == hl_attr_table[i])) + || (!(hl_attr_table[i] & HL_UNDERLINE_MASK) + && (iarg & hl_attr_table[i]))) { if (buf[0] != NUL) { xstrlcat(buf, ",", 100); } xstrlcat(buf, hl_name_table[i], 100); - iarg &= ~hl_attr_table[i]; // don't want "inverse" + if (!(hl_attr_table[i] & HL_UNDERLINE_MASK)) { + iarg &= ~hl_attr_table[i]; // don't want "inverse" + } } } } diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 5ffd90fddd..1415ceeaed 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -64,6 +64,7 @@ #include "nvim/window.h" static int in_fast_callback = 0; +static bool in_script = false; // Initialized in nlua_init(). static lua_State *global_lstate = NULL; @@ -133,8 +134,13 @@ static void nlua_error(lua_State *const lstate, const char *const msg) str = lua_tolstring(lstate, -1, &len); } - msg_ext_set_kind("lua_error"); - semsg_multiline(msg, (int)len, str); + if (in_script) { + os_errmsg(str); + os_errmsg("\n"); + } else { + msg_ext_set_kind("lua_error"); + semsg_multiline(msg, (int)len, str); + } lua_pop(lstate, 1); } @@ -534,7 +540,7 @@ int nlua_get_global_ref_count(void) return nlua_global_refs->ref_count; } -static void nlua_common_vim_init(lua_State *lstate, bool is_thread) +static void nlua_common_vim_init(lua_State *lstate, bool is_thread, bool is_standalone) FUNC_ATTR_NONNULL_ARG(1) { nlua_ref_state_t *ref_state = nlua_new_ref_state(lstate, is_thread); @@ -567,7 +573,9 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread) lua_setfield(lstate, -2, "_empty_dict_mt"); // vim.loop - if (is_thread) { + if (is_standalone) { + // do nothing, use libluv like in a standalone interpreter + } else if (is_thread) { luv_set_callback(lstate, nlua_luv_thread_cb_cfpcall); luv_set_thread(lstate, nlua_luv_thread_cfpcall); luv_set_cthread(lstate, nlua_luv_thread_cfcpcall); @@ -606,7 +614,7 @@ static int nlua_module_preloader(lua_State *lstate) return 1; } -static bool nlua_init_packages(lua_State *lstate) +static bool nlua_init_packages(lua_State *lstate, bool is_standalone) FUNC_ATTR_NONNULL_ALL { // put builtin packages in preload @@ -618,7 +626,7 @@ static bool nlua_init_packages(lua_State *lstate) lua_pushcclosure(lstate, nlua_module_preloader, 1); // [package, preload, cclosure] lua_setfield(lstate, -2, def.name); // [package, preload] - if (nlua_disable_preload && strequal(def.name, "vim.inspect")) { + if ((nlua_disable_preload && !is_standalone) && strequal(def.name, "vim.inspect")) { break; } } @@ -769,7 +777,7 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_ui_detach); lua_setfield(lstate, -2, "ui_detach"); - nlua_common_vim_init(lstate, false); + nlua_common_vim_init(lstate, false, false); // patch require() (only for --startuptime) if (time_fd != NULL) { @@ -788,7 +796,7 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setglobal(lstate, "vim"); - if (!nlua_init_packages(lstate)) { + if (!nlua_init_packages(lstate, false)) { return false; } @@ -813,6 +821,9 @@ void nlua_init(char **argv, int argc, int lua_arg0) luaL_openlibs(lstate); if (!nlua_state_init(lstate)) { os_errmsg(_("E970: Failed to initialize builtin lua modules\n")); +#ifdef EXITFREE + nlua_common_free_all_mem(lstate); +#endif os_exit(1); } @@ -824,9 +835,28 @@ void nlua_init(char **argv, int argc, int lua_arg0) static lua_State *nlua_thread_acquire_vm(void) { + return nlua_init_state(true); +} + +void nlua_run_script(char **argv, int argc, int lua_arg0) + FUNC_ATTR_NORETURN +{ + in_script = true; + global_lstate = nlua_init_state(false); + luv_set_thread_cb(nlua_thread_acquire_vm, nlua_common_free_all_mem); + nlua_init_argv(global_lstate, argv, argc, lua_arg0); + bool lua_ok = nlua_exec_file(argv[lua_arg0 - 1]); +#ifdef EXITFREE + nlua_free_all_mem(); +#endif + exit(lua_ok ? 0 : 1); +} + +lua_State *nlua_init_state(bool thread) +{ // If it is called from the main thread, it will attempt to rebuild the cache. const uv_thread_t self = uv_thread_self(); - if (uv_thread_equal(&main_thread, &self)) { + if (!in_script && uv_thread_equal(&main_thread, &self)) { runtime_search_path_validate(); } @@ -835,9 +865,11 @@ static lua_State *nlua_thread_acquire_vm(void) // Add in the lua standard libraries luaL_openlibs(lstate); - // print - lua_pushcfunction(lstate, &nlua_print); - lua_setglobal(lstate, "print"); + if (!in_script) { + // print + lua_pushcfunction(lstate, &nlua_print); + lua_setglobal(lstate, "print"); + } lua_pushinteger(lstate, 0); lua_setfield(lstate, LUA_REGISTRYINDEX, "nlua.refcount"); @@ -845,18 +877,20 @@ static lua_State *nlua_thread_acquire_vm(void) // vim lua_newtable(lstate); - nlua_common_vim_init(lstate, true); + nlua_common_vim_init(lstate, thread, in_script); nlua_state_add_stdlib(lstate, true); - lua_createtable(lstate, 0, 0); - lua_pushcfunction(lstate, nlua_thr_api_nvim__get_runtime); - lua_setfield(lstate, -2, "nvim__get_runtime"); - lua_setfield(lstate, -2, "api"); + if (!in_script) { + lua_createtable(lstate, 0, 0); + lua_pushcfunction(lstate, nlua_thr_api_nvim__get_runtime); + lua_setfield(lstate, -2, "nvim__get_runtime"); + lua_setfield(lstate, -2, "api"); + } lua_setglobal(lstate, "vim"); - nlua_init_packages(lstate); + nlua_init_packages(lstate, in_script); lua_getglobal(lstate, "package"); lua_getfield(lstate, -1, "loaded"); diff --git a/src/nvim/main.c b/src/nvim/main.c index bbe877356d..e26922bf8e 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -239,6 +239,14 @@ int main(int argc, char **argv) argv0 = argv[0]; + if (argc > 1 && STRICMP(argv[1], "-ll") == 0) { + if (argc == 2) { + print_mainerr(err_arg_missing, argv[1]); + exit(1); + } + nlua_run_script(argv, argc, 3); + } + char *fname = NULL; // file name from command line mparm_T params; // various parameters passed between // main() and other functions. @@ -803,6 +811,11 @@ void preserve_exit(void) really_exiting = true; // Ignore SIGHUP while we are already exiting. #9274 signal_reject_deadly(); + + if (ui_client_channel_id) { + os_exit(1); + } + os_errmsg(IObuff); os_errmsg("\n"); ui_flush(); @@ -2111,6 +2124,12 @@ static int execute_env(char *env) static void mainerr(const char *errstr, const char *str) FUNC_ATTR_NORETURN { + print_mainerr(errstr, str); + os_exit(1); +} + +static void print_mainerr(const char *errstr, const char *str) +{ char *prgname = path_tail(argv0); signal_stop(); // kill us with CTRL-C here, if you like @@ -2126,8 +2145,6 @@ static void mainerr(const char *errstr, const char *str) os_errmsg(_("\nMore info with \"")); os_errmsg(prgname); os_errmsg(" -h\"\n"); - - os_exit(1); } /// Prints version information for "nvim -v" or "nvim --version". diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 2036ddd21d..77ba6e6fa4 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -1182,7 +1182,7 @@ static size_t check_node(MarkTree *b, mtnode_t *x, mtpos_t *last, bool *last_rig assert(x->ptr[i] != x->ptr[j]); } } - } else { + } else if (x->n > 0) { *last = x->key[x->n - 1].pos; } return n_keys; diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 10a8195e7a..b3fc64a68c 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -149,7 +149,7 @@ struct data_block { #define DB_INDEX_MASK (~DB_MARKED) #define INDEX_SIZE (sizeof(unsigned)) // size of one db_index entry -#define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) // size of data block header +#define HEADER_SIZE (offsetof(DATA_BL, db_index)) // size of data block header enum { B0_FNAME_SIZE_ORG = 900, // what it was in older versions @@ -2720,7 +2720,8 @@ static bhdr_T *ml_new_ptr(memfile_T *mfp) PTR_BL *pp = hp->bh_data; pp->pb_id = PTR_ID; pp->pb_count = 0; - pp->pb_count_max = (uint16_t)((mfp->mf_page_size - sizeof(PTR_BL)) / sizeof(PTR_EN) + 1); + pp->pb_count_max + = (uint16_t)((mfp->mf_page_size - offsetof(PTR_BL, pb_pointer)) / sizeof(PTR_EN)); return hp; } diff --git a/src/nvim/message.c b/src/nvim/message.c index 7f29b19031..3b3dfcd5b6 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -2514,7 +2514,7 @@ static void store_sb_text(char **sb_str, char *s, int attr, int *sb_col, int fin } if (s > *sb_str) { - mp = xmalloc((sizeof(msgchunk_T) + (size_t)(s - *sb_str))); + mp = xmalloc(offsetof(msgchunk_T, sb_text) + (size_t)(s - *sb_str) + 1); mp->sb_eol = (char)finish; mp->sb_msg_col = *sb_col; mp->sb_attr = attr; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 58a18ca5a8..b88cfb8926 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5072,9 +5072,13 @@ static void nv_visual(cmdarg_T *cap) curwin->w_curswant = MAXCOL; coladvance(MAXCOL); } else if (VIsual_mode == Ctrl_V) { + // Update curswant on the original line, that is where "col" is valid. + linenr_T lnum = curwin->w_cursor.lnum; + curwin->w_cursor.lnum = VIsual.lnum; update_curswant_force(); assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX); curwin->w_curswant += resel_VIsual_vcol * (int)cap->count0 - 1; + curwin->w_cursor.lnum = lnum; coladvance(curwin->w_curswant); } else { curwin->w_set_curswant = true; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 93449f2337..86e317b7f6 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3203,6 +3203,56 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) recursive = false; } +/// Execute autocommands for TextPutPost. +/// +/// @param oap Operator arguments. +/// @param reg The yank register used. +static void do_autocmd_textputpost(int regname, yankreg_T *reg) + FUNC_ATTR_NONNULL_ALL +{ + static bool recursive = false; + int len; + + if (recursive || !has_event(EVENT_TEXTPUTPOST)) { + // No autocommand was defined, or we yanked from this autocommand. + return; + } + + recursive = true; + + save_v_event_T save_v_event; + // Set the v:event dictionary with information about the yank. + dict_T *dict = get_v_event(&save_v_event); + + // The yanked text contents. + list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size); + for (size_t i = 0; i < reg->y_size; i++) { + tv_list_append_string(list, (const char *)reg->y_array[i], -1); + } + tv_list_set_lock(list, VAR_FIXED); + (void)tv_dict_add_list(dict, S_LEN("regcontents"), list); + + // Register type. + char buf[NUMBUFLEN + 6]; + format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); + (void)tv_dict_add_str(dict, S_LEN("regtype"), buf); + + // Name of requested register, or empty string for unnamed operation. + len = (*utf_char2len)(regname); + buf[len] = 0; + utf_char2bytes(regname, buf); + recursive = true; + (void)tv_dict_add_str(dict, S_LEN("regname"), buf); + + tv_dict_set_keys_readonly(dict); + textlock++; + apply_autocmds(EVENT_TEXTPUTPOST, NULL, NULL, false, curbuf); + textlock--; + restore_v_event(dict, &save_v_event); + + recursive = false; +} + /// Put contents of register "regname" into the text. /// Caller must check "regname" to be valid! /// @@ -3990,6 +4040,8 @@ error: curwin->w_set_curswant = true; end: + do_autocmd_textputpost(regname, reg); + if (cmdmod.cmod_flags & CMOD_LOCKMARKS) { curbuf->b_op_start = orig_start; curbuf->b_op_end = orig_end; diff --git a/src/nvim/option.c b/src/nvim/option.c index e63665f7c4..86840faa43 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -726,11 +726,173 @@ void ex_set(exarg_T *eap) (void)do_set(eap->arg, flags); } +static void do_set_bool(int opt_idx, int opt_flags, int prefix, int nextchar, const char *varp, + char **errmsg) +{ + varnumber_T value; + + // ":set opt!": invert + // ":set opt&": reset to default value + // ":set opt<": reset to global value + if (nextchar == '!') { + value = *(int *)(varp) ^ 1; + } else if (nextchar == '&') { + value = (int)(intptr_t)options[opt_idx].def_val; + } else if (nextchar == '<') { + // For 'autoread' -1 means to use global value. + if ((int *)varp == &curbuf->b_p_ar && opt_flags == OPT_LOCAL) { + value = -1; + } else { + value = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + } else { + if (prefix == 2) { + value = *(int *)varp ^ 1; // ":set invopt": invert + } else { + value = prefix; // ":set opt" or ":set noopt": set or reset + } + } + + *errmsg = set_bool_option(opt_idx, (char_u *)varp, (int)value, opt_flags); +} + +static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, const set_op_T op, + const char *varp, char *errbuf, size_t errbuflen, char **errmsg) +{ + varnumber_T value; + char *arg = *argp; + + // Different ways to set a number option: + // & set to default value + // < set to global value + // <xx> accept special key codes for 'wildchar' + // c accept any non-digit for 'wildchar' + // [-]0-9 set number + // other error + arg++; + if (nextchar == '&') { + value = (long)(intptr_t)options[opt_idx].def_val; + } else if (nextchar == '<') { + // For 'undolevels' NO_LOCAL_UNDOLEVEL means to + // use the global value. + if ((long *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) { + value = NO_LOCAL_UNDOLEVEL; + } else { + value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + } else if (((long *)varp == &p_wc + || (long *)varp == &p_wcm) + && (*arg == '<' + || *arg == '^' + || (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1])) + && !ascii_isdigit(*arg)))) { + value = string_to_key(arg); + if (value == 0 && (long *)varp != &p_wcm) { + *errmsg = e_invarg; + return; + } + } else if (*arg == '-' || ascii_isdigit(*arg)) { + int i; + // Allow negative, octal and hex numbers. + vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true); + if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) { + *errmsg = e_number_required_after_equal; + return; + } + } else { + *errmsg = e_number_required_after_equal; + return; + } + + if (op == OP_ADDING) { + value = *(long *)varp + value; + } + if (op == OP_PREPENDING) { + value = *(long *)varp * value; + } + if (op == OP_REMOVING) { + value = *(long *)varp - value; + } + *errmsg = set_num_option(opt_idx, (char_u *)varp, (long)value, + errbuf, errbuflen, opt_flags); +} + +// Handle some special cases with string option values +static void munge_string_opt_val(char **varp, char **oldval, char **const origval, + char_u **const origval_l, char_u **const origval_g, + char **const argp, char *const whichwrap, size_t whichwraplen, + char **const save_argp) +{ + // Set 'keywordprg' to ":help" if an empty + // value was passed to :set by the user. + if (varp == &p_kp && (**argp == NUL || **argp == ' ')) { + *save_argp = *argp; + *argp = ":help"; + } else if (varp == &p_bs && ascii_isdigit(**(char_u **)varp)) { + // Convert 'backspace' number to string, for + // adding, prepending and removing string. + const int i = getdigits_int(varp, true, 0); + switch (i) { + case 0: + *varp = empty_option; + break; + case 1: + *varp = xstrdup("indent,eol"); + break; + case 2: + *varp = xstrdup("indent,eol,start"); + break; + case 3: + *varp = xstrdup("indent,eol,nostop"); + break; + } + xfree(*oldval); + if (*origval == *oldval) { + *origval = *varp; + } + if (*origval_l == (char_u *)(*oldval)) { + *origval_l = *(char_u **)varp; + } + if (*origval_g == (char_u *)(*oldval)) { + *origval_g = *(char_u **)varp; + } + *oldval = *varp; + } else if (varp == &p_ww && ascii_isdigit(**argp)) { + // Convert 'whichwrap' number to string, for backwards compatibility + // with Vim 3.0. + *whichwrap = NUL; + int i = getdigits_int(argp, true, 0); + if (i & 1) { + xstrlcat(whichwrap, "b,", whichwraplen); + } + if (i & 2) { + xstrlcat(whichwrap, "s,", whichwraplen); + } + if (i & 4) { + xstrlcat(whichwrap, "h,l,", whichwraplen); + } + if (i & 8) { + xstrlcat(whichwrap, "<,>,", whichwraplen); + } + if (i & 16) { + xstrlcat(whichwrap, "[,],", whichwraplen); + } + if (*whichwrap != NUL) { // remove trailing , + whichwrap[strlen(whichwrap) - 1] = NUL; + } + *save_argp = *argp; + *argp = whichwrap; + } else if (**argp == '>' && (varp == &p_dir || varp == &p_bdir)) { + // Remove '>' before 'dir' and 'bdir', for backwards compatibility with + // version 3.0 + (*argp)++; + } +} + /// Part of do_set() for string options. -/// @return FAIL on failure, do not process further options. -static int do_set_string(int opt_idx, int opt_flags, char **argp, int nextchar, set_op_T op_arg, - uint32_t flags, char *varp_arg, char *errbuf, size_t errbuflen, - int *value_checked, char **errmsg) +static void do_set_string(int opt_idx, int opt_flags, char **argp, int nextchar, set_op_T op_arg, + uint32_t flags, char *varp_arg, char *errbuf, size_t errbuflen, + int *value_checked, char **errmsg) { char *arg = *argp; set_op_T op = op_arg; @@ -794,70 +956,8 @@ static int do_set_string(int opt_idx, int opt_flags, char **argp, int nextchar, } else { arg++; // jump to after the '=' or ':' - // Set 'keywordprg' to ":help" if an empty - // value was passed to :set by the user. - if (varp == (char *)&p_kp && (*arg == NUL || *arg == ' ')) { - save_arg = arg; - arg = ":help"; - } else if (varp == (char *)&p_bs && ascii_isdigit(**(char_u **)varp)) { - // Convert 'backspace' number to string, for - // adding, prepending and removing string. - int i = getdigits_int((char **)varp, true, 0); - switch (i) { - case 0: - *(char **)varp = empty_option; - break; - case 1: - *(char_u **)varp = (char_u *)xstrdup("indent,eol"); - break; - case 2: - *(char_u **)varp = (char_u *)xstrdup("indent,eol,start"); - break; - case 3: - *(char_u **)varp = (char_u *)xstrdup("indent,eol,nostop"); - break; - } - xfree(oldval); - if (origval == oldval) { - origval = *(char **)varp; - } - if (origval_l == (char_u *)oldval) { - origval_l = *(char_u **)varp; - } - if (origval_g == (char_u *)oldval) { - origval_g = *(char_u **)varp; - } - oldval = *(char **)varp; - } else if (varp == (char *)&p_ww && ascii_isdigit(*arg)) { - // Convert 'whichwrap' number to string, for backwards compatibility - // with Vim 3.0. - *whichwrap = NUL; - int i = getdigits_int(&arg, true, 0); - if (i & 1) { - xstrlcat(whichwrap, "b,", sizeof(whichwrap)); - } - if (i & 2) { - xstrlcat(whichwrap, "s,", sizeof(whichwrap)); - } - if (i & 4) { - xstrlcat(whichwrap, "h,l,", sizeof(whichwrap)); - } - if (i & 8) { - xstrlcat(whichwrap, "<,>,", sizeof(whichwrap)); - } - if (i & 16) { - xstrlcat(whichwrap, "[,],", sizeof(whichwrap)); - } - if (*whichwrap != NUL) { // remove trailing , - whichwrap[strlen(whichwrap) - 1] = NUL; - } - save_arg = arg; - arg = whichwrap; - } else if (*arg == '>' && (varp == (char *)&p_dir || varp == (char *)&p_bdir)) { - // Remove '>' before 'dir' and 'bdir', for backwards compatibility with - // version 3.0 - arg++; - } + munge_string_opt_val((char **)varp, &oldval, &origval, &origval_l, &origval_g, &arg, + whichwrap, sizeof(whichwrap), &save_arg); // Copy the new string into allocated memory. // Can't use set_string_option_direct(), because we need to remove the @@ -1059,416 +1159,391 @@ static int do_set_string(int opt_idx, int opt_flags, char **argp, int nextchar, xfree(saved_newval); *argp = arg; - return *errmsg == NULL ? OK : FAIL; } -/// Parse 'arg' for option settings. -/// -/// 'arg' may be IObuff, but only when no errors can be present and option -/// does not need to be expanded with option_expand(). -/// "opt_flags": -/// 0 for ":set" -/// OPT_GLOBAL for ":setglobal" -/// OPT_LOCAL for ":setlocal" and a modeline -/// OPT_MODELINE for a modeline -/// OPT_WINONLY to only set window-local options -/// OPT_NOWIN to skip setting window-local options -/// -/// @param arg option string (may be written to!) -/// +static set_op_T get_op(const char *arg) +{ + set_op_T op = OP_NONE; + if (*arg != NUL && *(arg + 1) == '=') { + if (*arg == '+') { + op = OP_ADDING; // "+=" + } else if (*arg == '^') { + op = OP_PREPENDING; // "^=" + } else if (*arg == '-') { + op = OP_REMOVING; // "-=" + } + } + return op; +} + +static int get_option_prefix(char **argp) +{ + if (strncmp(*argp, "no", 2) == 0) { + *argp += 2; + return 0; + } else if (strncmp(*argp, "inv", 3) == 0) { + *argp += 3; + return 2; + } + + return 1; +} + +/// @param[in] arg Pointer to start option name +/// @param[out] opt_idxp Option index in options[] table. +/// @param[out] keyp +/// @param[out] len Length of option name /// @return FAIL if an error is detected, OK otherwise -int do_set(char *arg, int opt_flags) +static int parse_option_name(char *arg, int *keyp, int *lenp, int *opt_idxp) { - int did_show = false; // already showed one value + // find end of name + int key = 0; + int len; + int opt_idx; - if (*arg == NUL) { - showoptions(0, opt_flags); - did_show = true; - goto theend; - } - - char errbuf[80]; - - while (*arg != NUL) { // loop to process all options - char *errmsg = NULL; - char *startarg = arg; // remember for error message - - if (strncmp(arg, "all", 3) == 0 && !ASCII_ISALPHA(arg[3]) - && !(opt_flags & OPT_MODELINE)) { - // ":set all" show all options. - // ":set all&" set all options to their default value. - arg += 3; - if (*arg == '&') { - arg++; - // Only for :set command set global value of local options. - set_options_default(OPT_FREE | opt_flags); - didset_options(); - didset_options2(); - ui_refresh_options(); - redraw_all_later(UPD_CLEAR); - } else { - showoptions(1, opt_flags); - did_show = true; - } + if (*arg == '<') { + opt_idx = -1; + // look out for <t_>;> + if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4]) { + len = 5; } else { - int prefix = 1; // 1: nothing, 0: "no", 2: "inv" in front of name - if (strncmp(arg, "no", 2) == 0) { - prefix = 0; - arg += 2; - } else if (strncmp(arg, "inv", 3) == 0) { - prefix = 2; - arg += 3; + len = 1; + while (arg[len] != NUL && arg[len] != '>') { + len++; } - - // find end of name - int key = 0; - int len; - int opt_idx; - if (*arg == '<') { - opt_idx = -1; - // look out for <t_>;> - if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4]) { - len = 5; - } else { - len = 1; - while (arg[len] != NUL && arg[len] != '>') { - len++; - } - } - if (arg[len] != '>') { - errmsg = e_invarg; - goto skip; - } - if (arg[1] == 't' && arg[2] == '_') { // could be term code - opt_idx = findoption_len((const char *)arg + 1, (size_t)(len - 1)); - } + } + if (arg[len] != '>') { + return FAIL; + } + if (arg[1] == 't' && arg[2] == '_') { // could be term code + opt_idx = findoption_len((const char *)arg + 1, (size_t)(len - 1)); + } + len++; + if (opt_idx == -1) { + key = find_key_option(arg + 1, true); + } + } else { + // The two characters after "t_" may not be alphanumeric. + if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) { + len = 4; + } else { + len = 0; + while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') { len++; - if (opt_idx == -1) { - key = find_key_option(arg + 1, true); - } - } else { - len = 0; - // The two characters after "t_" may not be alphanumeric. - if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3]) { - len = 4; - } else { - while (ASCII_ISALNUM(arg[len]) || arg[len] == '_') { - len++; - } - } - opt_idx = findoption_len((const char *)arg, (size_t)len); - if (opt_idx == -1) { - key = find_key_option(arg, false); - } } + } + opt_idx = findoption_len((const char *)arg, (size_t)len); + if (opt_idx == -1) { + key = find_key_option(arg, false); + } + } - // remember character after option name - int afterchar = (uint8_t)arg[len]; + *keyp = key; + *lenp = len; + *opt_idxp = opt_idx; - // skip white space, allow ":set ai ?" - while (ascii_iswhite(arg[len])) { - len++; - } + return OK; +} - set_op_T op = OP_NONE; - if (arg[len] != NUL && arg[len + 1] == '=') { - if (arg[len] == '+') { - op = OP_ADDING; // "+=" - len++; - } else if (arg[len] == '^') { - op = OP_PREPENDING; // "^=" - len++; - } else if (arg[len] == '-') { - op = OP_REMOVING; // "-=" - len++; - } - } - char_u nextchar = (uint8_t)arg[len]; // next non-white char after option name +static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t flags, int prefix, + char **errmsg) +{ + // Only bools can have a prefix of 'inv' or 'no' + if (!(flags & P_BOOL) && prefix != 1) { + *errmsg = e_invarg; + return FAIL; + } - if (opt_idx == -1 && key == 0) { // found a mismatch: skip - errmsg = e_unknown_option; - goto skip; - } + // Skip all options that are not window-local (used when showing + // an already loaded buffer in a window). + if ((opt_flags & OPT_WINONLY) + && (opt_idx < 0 || options[opt_idx].var != VAR_WIN)) { + return FAIL; + } - uint32_t flags; // flags for current option - char *varp = NULL; // pointer to variable for current option - - if (opt_idx >= 0) { - if (options[opt_idx].var == NULL) { // hidden option: skip - // Only give an error message when requesting the value of - // a hidden option, ignore setting it. - if (vim_strchr("=:!&<", (uint8_t)nextchar) == NULL - && (!(options[opt_idx].flags & P_BOOL) - || nextchar == '?')) { - errmsg = e_unsupportedoption; - } - goto skip; - } + // Skip all options that are window-local (used for :vimgrep). + if ((opt_flags & OPT_NOWIN) && opt_idx >= 0 + && options[opt_idx].var == VAR_WIN) { + return FAIL; + } - flags = options[opt_idx].flags; - varp = get_varp_scope(&(options[opt_idx]), opt_flags); - } else { - flags = P_STRING; - } + // Disallow changing some options from modelines. + if (opt_flags & OPT_MODELINE) { + if (flags & (P_SECURE | P_NO_ML)) { + *errmsg = e_not_allowed_in_modeline; + return FAIL; + } + if ((flags & P_MLE) && !p_mle) { + *errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off; + return FAIL; + } + // In diff mode some options are overruled. This avoids that + // 'foldmethod' becomes "marker" instead of "diff" and that + // "wrap" gets set. + if (win->w_p_diff + && opt_idx >= 0 // shut up coverity warning + && (options[opt_idx].indir == PV_FDM + || options[opt_idx].indir == PV_WRAP)) { + return FAIL; + } + } - // Skip all options that are not window-local (used when showing - // an already loaded buffer in a window). - if ((opt_flags & OPT_WINONLY) - && (opt_idx < 0 || options[opt_idx].var != VAR_WIN)) { - goto skip; - } + // Disallow changing some options in the sandbox + if (sandbox != 0 && (flags & P_SECURE)) { + *errmsg = e_sandbox; + return FAIL; + } - // Skip all options that are window-local (used for :vimgrep). - if ((opt_flags & OPT_NOWIN) && opt_idx >= 0 - && options[opt_idx].var == VAR_WIN) { - goto skip; - } + return OK; +} - // Disallow changing some options from modelines. - if (opt_flags & OPT_MODELINE) { - if (flags & (P_SECURE | P_NO_ML)) { - errmsg = e_not_allowed_in_modeline; - goto skip; - } - if ((flags & P_MLE) && !p_mle) { - errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off; - goto skip; - } - // In diff mode some options are overruled. This avoids that - // 'foldmethod' becomes "marker" instead of "diff" and that - // "wrap" gets set. - if (curwin->w_p_diff - && opt_idx >= 0 // shut up coverity warning - && (options[opt_idx].indir == PV_FDM - || options[opt_idx].indir == PV_WRAP)) { - goto skip; - } - } +static void do_set_option_value(int opt_idx, int opt_flags, char **argp, int prefix, int nextchar, + set_op_T op, uint32_t flags, char *varp, char *errbuf, + size_t errbuflen, char **errmsg) +{ + int value_checked = false; + if (flags & P_BOOL) { // boolean + do_set_bool(opt_idx, opt_flags, prefix, nextchar, varp, errmsg); + } else if (flags & P_NUM) { // numeric + do_set_num(opt_idx, opt_flags, argp, nextchar, op, varp, errbuf, errbuflen, errmsg); + } else if (opt_idx >= 0) { // string. + do_set_string(opt_idx, opt_flags, argp, nextchar, op, flags, varp, errbuf, + errbuflen, &value_checked, errmsg); + } else { + // key code option(FIXME(tarruda): Show a warning or something + // similar) + } - // Disallow changing some options in the sandbox - if (sandbox != 0 && (flags & P_SECURE)) { - errmsg = e_sandbox; - goto skip; - } + if (*errmsg != NULL) { + return; + } - if (vim_strchr("?=:!&<", (uint8_t)nextchar) != NULL) { - arg += len; - if (nextchar == '&' && arg[1] == 'v' && arg[2] == 'i') { - if (arg[3] == 'm') { // "opt&vim": set to Vim default - arg += 3; - } else { // "opt&vi": set to Vi default - arg += 2; - } - } - if (vim_strchr("?!&<", (uint8_t)nextchar) != NULL - && arg[1] != NUL && !ascii_iswhite(arg[1])) { - errmsg = e_trailing; - goto skip; - } - } + if (opt_idx >= 0) { + did_set_option(opt_idx, opt_flags, op == OP_NONE, value_checked); + } +} - // - // allow '=' and ':' as MS-DOS command.com allows only one - // '=' character per "set" command line. grrr. (jw) - // - if (nextchar == '?' - || (prefix == 1 - && vim_strchr("=:&<", (uint8_t)nextchar) == NULL - && !(flags & P_BOOL))) { - // print value - if (did_show) { - msg_putchar('\n'); // cursor below last one - } else { - gotocmdline(true); // cursor at status line - did_show = true; // remember that we did a line - } - if (opt_idx >= 0) { - showoneopt(&options[opt_idx], opt_flags); - if (p_verbose > 0) { - // Mention where the option was last set. - if (varp == (char *)options[opt_idx].var) { - option_last_set_msg(options[opt_idx].last_set); - } else if ((int)options[opt_idx].indir & PV_WIN) { - option_last_set_msg(curwin->w_p_script_ctx[ - (int)options[opt_idx].indir & PV_MASK]); - } else if ((int)options[opt_idx].indir & PV_BUF) { - option_last_set_msg(curbuf->b_p_script_ctx[ - (int)options[opt_idx].indir & PV_MASK]); - } - } - } else { - errmsg = e_key_code_not_set; - goto skip; - } - if (nextchar != '?' - && nextchar != NUL && !ascii_iswhite(afterchar)) { - errmsg = e_trailing; - } - } else { - int value_checked = false; - varnumber_T value; +static void do_set_option(int opt_flags, char **argp, bool *did_show, char *errbuf, + size_t errbuflen, char **errmsg) +{ + // 1: nothing, 0: "no", 2: "inv" in front of name + int prefix = get_option_prefix(argp); - if (flags & P_BOOL) { // boolean - if (nextchar == '=' || nextchar == ':') { - errmsg = e_invarg; - goto skip; - } + char *arg = *argp; - // ":set opt!": invert - // ":set opt&": reset to default value - // ":set opt<": reset to global value - if (nextchar == '!') { - value = *(int *)(varp) ^ 1; - } else if (nextchar == '&') { - value = (int)(intptr_t)options[opt_idx].def_val; - } else if (nextchar == '<') { - // For 'autoread' -1 means to use global value. - if ((int *)varp == &curbuf->b_p_ar - && opt_flags == OPT_LOCAL) { - value = -1; - } else { - value = *(int *)get_varp_scope(&(options[opt_idx]), - OPT_GLOBAL); - } - } else { - // ":set invopt": invert - // ":set opt" or ":set noopt": set or reset - if (nextchar != NUL && !ascii_iswhite(afterchar)) { - errmsg = e_trailing; - goto skip; - } - if (prefix == 2) { // inv - value = *(int *)(varp) ^ 1; - } else { - value = prefix; - } - } + // find end of name + int key = 0; + int len; + int opt_idx; + if (parse_option_name(arg, &key, &len, &opt_idx) == FAIL) { + *errmsg = e_invarg; + return; + } - errmsg = set_bool_option(opt_idx, (char_u *)varp, (int)value, opt_flags); - } else { // Numeric or string. - if (vim_strchr("=:&<", (uint8_t)nextchar) == NULL - || prefix != 1) { - errmsg = e_invarg; - goto skip; - } + // remember character after option name + int afterchar = (uint8_t)arg[len]; - if (flags & P_NUM) { // numeric - // Different ways to set a number option: - // & set to default value - // < set to global value - // <xx> accept special key codes for 'wildchar' - // c accept any non-digit for 'wildchar' - // [-]0-9 set number - // other error - arg++; - if (nextchar == '&') { - value = (long)(intptr_t)options[opt_idx].def_val; - } else if (nextchar == '<') { - // For 'undolevels' NO_LOCAL_UNDOLEVEL means to - // use the global value. - if ((long *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) { - value = NO_LOCAL_UNDOLEVEL; - } else { - value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); - } - } else if (((long *)varp == &p_wc - || (long *)varp == &p_wcm) - && (*arg == '<' - || *arg == '^' - || (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1])) - && !ascii_isdigit(*arg)))) { - value = string_to_key(arg); - if (value == 0 && (long *)varp != &p_wcm) { - errmsg = e_invarg; - goto skip; - } - } else if (*arg == '-' || ascii_isdigit(*arg)) { - int i; - // Allow negative, octal and hex numbers. - vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true); - if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) { - errmsg = e_number_required_after_equal; - goto skip; - } - } else { - errmsg = e_number_required_after_equal; - goto skip; - } + // skip white space, allow ":set ai ?" + while (ascii_iswhite(arg[len])) { + len++; + } - if (op == OP_ADDING) { - value = *(long *)varp + value; - } - if (op == OP_PREPENDING) { - value = *(long *)varp * value; - } - if (op == OP_REMOVING) { - value = *(long *)varp - value; - } - errmsg = set_num_option(opt_idx, (char_u *)varp, (long)value, - errbuf, sizeof(errbuf), - opt_flags); - } else if (opt_idx >= 0) { // String. - if (do_set_string(opt_idx, opt_flags, &arg, nextchar, - op, flags, varp, errbuf, sizeof(errbuf), - &value_checked, &errmsg) == FAIL) { - if (errmsg != NULL) { - goto skip; - } - break; - } - } else { - // key code option(FIXME(tarruda): Show a warning or something - // similar) - } - } + set_op_T op = get_op(arg + len); + if (op != OP_NONE) { + len++; + } - if (opt_idx >= 0) { - did_set_option(opt_idx, opt_flags, op == OP_NONE, value_checked); - } + uint8_t nextchar = (uint8_t)arg[len]; // next non-white char after option name + + if (opt_idx == -1 && key == 0) { // found a mismatch: skip + *errmsg = e_unknown_option; + return; + } + + uint32_t flags; // flags for current option + char *varp = NULL; // pointer to variable for current option + + if (opt_idx >= 0) { + if (options[opt_idx].var == NULL) { // hidden option: skip + // Only give an error message when requesting the value of + // a hidden option, ignore setting it. + if (vim_strchr("=:!&<", nextchar) == NULL + && (!(options[opt_idx].flags & P_BOOL) + || nextchar == '?')) { + *errmsg = e_unsupportedoption; } + return; + } -skip: - // Advance to next argument. - // - skip until a blank found, taking care of backslashes - // - skip blanks - // - skip one "=val" argument (for hidden options ":set gfn =xx") - for (int i = 0; i < 2; i++) { - while (*arg != NUL && !ascii_iswhite(*arg)) { - if (*arg++ == '\\' && *arg != NUL) { - arg++; - } - } - arg = skipwhite(arg); - if (*arg != '=') { - break; - } + flags = options[opt_idx].flags; + varp = get_varp_scope(&(options[opt_idx]), opt_flags); + } else { + flags = P_STRING; + } + + if (validate_opt_idx(curwin, opt_idx, opt_flags, flags, prefix, errmsg) == FAIL) { + return; + } + + if (vim_strchr("?=:!&<", nextchar) != NULL) { + *argp += len; + if (nextchar == '&' && (*argp)[1] == 'v' && (*argp)[2] == 'i') { + if ((*argp)[3] == 'm') { // "opt&vim": set to Vim default + *argp += 3; + } else { // "opt&vi": set to Vi default + *argp += 2; } } + if (vim_strchr("?!&<", nextchar) != NULL + && (*argp)[1] != NUL && !ascii_iswhite((*argp)[1])) { + *errmsg = e_trailing; + return; + } + } - if (errmsg != NULL) { - xstrlcpy(IObuff, _(errmsg), IOSIZE); - int i = (int)strlen(IObuff) + 2; - if (i + (arg - startarg) < IOSIZE) { - // append the argument with the error - STRCAT(IObuff, ": "); - assert(arg >= startarg); - memmove(IObuff + i, startarg, (size_t)(arg - startarg)); - IObuff[i + (arg - startarg)] = NUL; + // + // allow '=' and ':' as MS-DOS command.com allows only one + // '=' character per "set" command line. grrr. (jw) + // + if (nextchar == '?' + || (prefix == 1 + && vim_strchr("=:&<", nextchar) == NULL + && !(flags & P_BOOL))) { + // print value + if (*did_show) { + msg_putchar('\n'); // cursor below last one + } else { + gotocmdline(true); // cursor at status line + *did_show = true; // remember that we did a line + } + if (opt_idx >= 0) { + showoneopt(&options[opt_idx], opt_flags); + if (p_verbose > 0) { + // Mention where the option was last set. + if (varp == (char *)options[opt_idx].var) { + option_last_set_msg(options[opt_idx].last_set); + } else if ((int)options[opt_idx].indir & PV_WIN) { + option_last_set_msg(curwin->w_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); + } else if ((int)options[opt_idx].indir & PV_BUF) { + option_last_set_msg(curbuf->b_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]); + } } - // make sure all characters are printable - trans_characters(IObuff, IOSIZE); + } else { + *errmsg = e_key_code_not_set; + return; + } + if (nextchar != '?' && nextchar != NUL && !ascii_iswhite(afterchar)) { + *errmsg = e_trailing; + } + return; + } - no_wait_return++; // wait_return() done later - emsg(IObuff); // show error highlighted - no_wait_return--; + if (flags & P_BOOL) { + if (vim_strchr("=:", nextchar) != NULL) { + *errmsg = e_invarg; + return; + } - return FAIL; + if (vim_strchr("!&<", nextchar) == NULL && nextchar != NUL && !ascii_iswhite(afterchar)) { + *errmsg = e_trailing; + return; } + } else { + if (vim_strchr("=:&<", nextchar) == NULL) { + *errmsg = e_invarg; + return; + } + } - arg = skipwhite(arg); + do_set_option_value(opt_idx, opt_flags, argp, prefix, nextchar, op, flags, varp, + errbuf, errbuflen, errmsg); +} + +/// Parse 'arg' for option settings. +/// +/// 'arg' may be IObuff, but only when no errors can be present and option +/// does not need to be expanded with option_expand(). +/// "opt_flags": +/// 0 for ":set" +/// OPT_GLOBAL for ":setglobal" +/// OPT_LOCAL for ":setlocal" and a modeline +/// OPT_MODELINE for a modeline +/// OPT_WINONLY to only set window-local options +/// OPT_NOWIN to skip setting window-local options +/// +/// @param arg option string (may be written to!) +/// +/// @return FAIL if an error is detected, OK otherwise +int do_set(char *arg, int opt_flags) +{ + bool did_show = false; // already showed one value + + if (*arg == NUL) { + showoptions(false, opt_flags); + did_show = true; + } else { + while (*arg != NUL) { // loop to process all options + if (strncmp(arg, "all", 3) == 0 && !ASCII_ISALPHA(arg[3]) + && !(opt_flags & OPT_MODELINE)) { + // ":set all" show all options. + // ":set all&" set all options to their default value. + arg += 3; + if (*arg == '&') { + arg++; + // Only for :set command set global value of local options. + set_options_default(OPT_FREE | opt_flags); + didset_options(); + didset_options2(); + ui_refresh_options(); + redraw_all_later(UPD_CLEAR); + } else { + showoptions(true, opt_flags); + did_show = true; + } + } else { + char *startarg = arg; // remember for error message + char *errmsg = NULL; + char errbuf[80]; + + do_set_option(opt_flags, &arg, &did_show, errbuf, sizeof(errbuf), &errmsg); + + // Advance to next argument. + // - skip until a blank found, taking care of backslashes + // - skip blanks + // - skip one "=val" argument (for hidden options ":set gfn =xx") + for (int i = 0; i < 2; i++) { + arg = skiptowhite_esc(arg); + arg = skipwhite(arg); + if (*arg != '=') { + break; + } + } + + if (errmsg != NULL) { + xstrlcpy(IObuff, _(errmsg), IOSIZE); + int i = (int)strlen(IObuff) + 2; + if (i + (arg - startarg) < IOSIZE) { + // append the argument with the error + STRCAT(IObuff, ": "); + assert(arg >= startarg); + memmove(IObuff + i, startarg, (size_t)(arg - startarg)); + IObuff[i + (arg - startarg)] = NUL; + } + // make sure all characters are printable + trans_characters(IObuff, IOSIZE); + + no_wait_return++; // wait_return() done later + emsg(IObuff); // show error highlighted + no_wait_return--; + + return FAIL; + } + } + + arg = skipwhite(arg); + } } -theend: if (silent_mode && did_show) { // After displaying option values in silent mode. silent_mode = false; @@ -2245,8 +2320,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, errmsg = e_positive; } } else if (pp == &p_ch) { - int minval = 0; - if (value < minval) { + if (value < 0) { errmsg = e_positive; } else { p_ch_was_zero = value == 0; @@ -3111,11 +3185,11 @@ static int find_key_option(const char *arg, bool has_lt) return find_key_option_len(arg, strlen(arg), has_lt); } -/// if 'all' == 0: show changed options -/// if 'all' == 1: show all normal options +/// if 'all' == false: show changed options +/// if 'all' == true: show all normal options /// /// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL -static void showoptions(int all, int opt_flags) +static void showoptions(bool all, int opt_flags) { #define INC 20 #define GAP 3 @@ -4832,12 +4906,12 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM return OK; } -void ExpandOldSetting(int *num_file, char ***file) +void ExpandOldSetting(int *numMatches, char ***matches) { char *var = NULL; - *num_file = 0; - *file = xmalloc(sizeof(char_u *)); + *numMatches = 0; + *matches = xmalloc(sizeof(char *)); // For a terminal key code expand_option_idx is < 0. if (expand_option_idx < 0) { @@ -4870,8 +4944,8 @@ void ExpandOldSetting(int *num_file, char ***file) } #endif - *file[0] = buf; - *num_file = 1; + *matches[0] = buf; + *numMatches = 1; } /// Get the value for the numeric or string option///opp in a nice format into diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index a5a708600f..ca50c3ab00 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -615,7 +615,7 @@ char *check_stl_option(char *s) continue; } if (vim_strchr(STL_ALL, (uint8_t)(*s)) == NULL) { - return illegal_char(errbuf, sizeof(errbuf), *s); + return illegal_char(errbuf, sizeof(errbuf), (uint8_t)(*s)); } if (*s == '{') { bool reevaluate = (*++s == '%'); @@ -638,13 +638,11 @@ char *check_stl_option(char *s) return NULL; } -static int shada_idx = -1; - +/// Check for a "normal" directory or file name in some options. Disallow a +/// path separator (slash and/or backslash), wildcards and characters that are +/// often illegal in a file name. Be more permissive if "secure" is off. static bool check_illegal_path_names(char *val, uint32_t flags) { - // Disallow a path separator (slash and/or backslash), wildcards and - // characters that are often illegal in a file name. Be more permissive - // if "secure" is off. return (((flags & P_NFNAME) && strpbrk(val, (secure ? "/\\*?[|;&<>\r\n" : "/\\*?[<>\r\n")) != NULL) || ((flags & P_NDNAME) @@ -701,8 +699,8 @@ static void did_set_breakindentopt(win_T *win, char **errmsg) static void did_set_isopt(buf_T *buf, bool *did_chartab, char **errmsg) { // 'isident', 'iskeyword', 'isprint or 'isfname' option: refill g_chartab[] - // If the new option is invalid, use old value. 'lisp' option: refill - // g_chartab[] for '-' char + // If the new option is invalid, use old value. + // 'lisp' option: refill g_chartab[] for '-' char if (buf_init_chartab(buf, true) == FAIL) { *did_chartab = true; // need to restore it below *errmsg = e_invarg; // error in value @@ -957,7 +955,7 @@ static void did_set_comments(char **varp, char *errbuf, size_t errbuflen, char * while (*s && *s != ':') { if (vim_strchr(COM_ALL, (uint8_t)(*s)) == NULL && !ascii_isdigit(*s) && *s != '-') { - *errmsg = illegal_char(errbuf, errbuflen, *s); + *errmsg = illegal_char(errbuf, errbuflen, (uint8_t)(*s)); break; } s++; @@ -1013,6 +1011,8 @@ static void did_set_verbosefile(char **errmsg) } } +static int shada_idx = -1; + static void did_set_shada(vimoption_T **opt, int *opt_idx, bool *free_oldval, char *errbuf, size_t errbuflen, char **errmsg) { @@ -1029,7 +1029,7 @@ static void did_set_shada(vimoption_T **opt, int *opt_idx, bool *free_oldval, ch for (char *s = p_shada; *s;) { // Check it's a valid character if (vim_strchr("!\"%'/:<@cfhnrs", (uint8_t)(*s)) == NULL) { - *errmsg = illegal_char(errbuf, errbuflen, *s); + *errmsg = illegal_char(errbuf, errbuflen, (uint8_t)(*s)); break; } if (*s == 'n') { // name is always last one @@ -1236,7 +1236,7 @@ static void did_set_complete(char **varp, char *errbuf, size_t errbuflen, char * break; } if (vim_strchr(".wbuksid]tU", (uint8_t)(*s)) == NULL) { - *errmsg = illegal_char(errbuf, errbuflen, *s); + *errmsg = illegal_char(errbuf, errbuflen, (uint8_t)(*s)); break; } if (*++s != NUL && *s != ',' && *s != ' ') { @@ -1272,6 +1272,16 @@ static void did_set_completeopt(char **errmsg) } } +#ifdef BACKSLASH_IN_FILENAME +static void did_set_completeslash(buf_T *buf, char **errmsg) +{ + if (check_opt_strings(p_csl, p_csl_values, false) != OK + || check_opt_strings(buf->b_p_csl, p_csl_values, false) != OK) { + *errmsg = e_invarg; + } +} +#endif + static void did_set_signcolumn(win_T *win, char **varp, const char *oldval, char **errmsg) { if (check_signcolumn(*varp) != OK) { @@ -1404,10 +1414,11 @@ static void did_set_virtualedit(win_T *win, int opt_flags, char *oldval, char ** } else { if (opt_strings_flags(ve, p_ve_values, flags, true) != OK) { *errmsg = e_invarg; - } else if (strcmp(p_ve, oldval) != 0) { + } else if (strcmp(ve, oldval) != 0) { // Recompute cursor position in case the new 've' setting // changes something. validate_virtcol_win(win); + // XXX: this only works when win == curwin coladvance(win->w_virtcol); } } @@ -1497,12 +1508,12 @@ static void did_set_vartabstop(buf_T *buf, win_T *win, char **varp, char **errms } } -static void did_set_optexpr(win_T *win, char **p_opt, char **varp, char **gvarp) +static void did_set_optexpr(char **varp) { - char *name = get_scriptlocal_funcname(*p_opt); + char *name = get_scriptlocal_funcname(*varp); if (name != NULL) { - free_string_option(*p_opt); - *p_opt = name; + free_string_option(*varp); + *varp = name; } } @@ -1511,8 +1522,8 @@ static void did_set_option_listflag(char **varp, char *flags, char *errbuf, size char **errmsg) { for (char *s = *varp; *s; s++) { - if (vim_strchr(flags, *s) == NULL) { - *errmsg = illegal_char(errbuf, errbuflen, *s); + if (vim_strchr(flags, (uint8_t)(*s)) == NULL) { + *errmsg = illegal_char(errbuf, errbuflen, (uint8_t)(*s)); break; } } @@ -1560,7 +1571,7 @@ static void do_filetype_autocmd(buf_T *buf, char **varp, int opt_flags, bool val } } -static void did_set_spelllang_source(win_T *win) +static void do_spelllang_source(win_T *win) { char fname[200]; char *q = win->w_s->b_p_spl; @@ -1613,243 +1624,238 @@ static char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx, char char **gvarp = (char **)get_varp_scope(opt, OPT_GLOBAL); // Disallow changing some options from secure mode - if ((secure || sandbox != 0) - && (opt->flags & P_SECURE)) { + if ((secure || sandbox != 0) && (opt->flags & P_SECURE)) { errmsg = e_secure; - } else if (check_illegal_path_names(*varp, opt->flags)) { // Check for a "normal" directory or file name in some options. + } else if (check_illegal_path_names(*varp, opt->flags)) { errmsg = e_invarg; - } else if (gvarp == &p_bkc) { // 'backupcopy' + } else if (gvarp == &p_bkc) { // 'backupcopy' did_set_backupcopy(buf, oldval, opt_flags, &errmsg); - } else if (varp == &p_bex || varp == &p_pm) { // 'backupext' and 'patchmode' + } else if (varp == &p_bex // 'backupext' + || varp == &p_pm) { // 'patchmode' did_set_backupext_or_patchmode(&errmsg); - } else if (varp == &win->w_p_briopt) { // 'breakindentopt' + } else if (varp == &win->w_p_briopt) { // 'breakindentopt' did_set_breakindentopt(win, &errmsg); - } else if (varp == &p_isi - || varp == &buf->b_p_isk - || varp == &p_isp - || varp == &p_isf) { - // 'isident', 'iskeyword', 'isprint or 'isfname' option + } else if (varp == &p_isi // 'isident' + || varp == &buf->b_p_isk // 'iskeyword' + || varp == &p_isp // 'isprint' + || varp == &p_isf) { // 'isfname' did_set_isopt(buf, &did_chartab, &errmsg); - } else if (varp == &p_hf) { // 'helpfile' + } else if (varp == &p_hf) { // 'helpfile' did_set_helpfile(); - } else if (varp == &p_rtp || varp == &p_pp) { // 'runtimepath' 'packpath' + } else if (varp == &p_rtp // 'runtimepath' + || varp == &p_pp) { // 'packpath' runtime_search_path_invalidate(); - } else if (varp == &win->w_p_culopt - || gvarp == &win->w_allbuf_opt.wo_culopt) { // 'cursorlineopt' + } else if (gvarp == &win->w_allbuf_opt.wo_culopt) { // 'cursorlineopt' did_set_cursorlineopt(win, varp, &errmsg); - } else if (varp == &win->w_p_cc) { // 'colorcolumn' + } else if (varp == &win->w_p_cc) { // 'colorcolumn' errmsg = check_colorcolumn(win); - } else if (varp == &p_hlg) { // 'helplang' + } else if (varp == &p_hlg) { // 'helplang' did_set_helplang(&errmsg); - } else if (varp == &p_hl) { // 'highlight' + } else if (varp == &p_hl) { // 'highlight' did_set_highlight(varp, &errmsg); - } else if (varp == &p_jop) { // 'jumpoptions' + } else if (varp == &p_jop) { // 'jumpoptions' did_set_opt_flags(p_jop, p_jop_values, &jop_flags, true, &errmsg); - } else if (gvarp == &p_nf) { // 'nrformats' + } else if (gvarp == &p_nf) { // 'nrformats' did_set_opt_strings(*varp, p_nf_values, true, &errmsg); - } else if (varp == &p_ssop) { // 'sessionoptions' + } else if (varp == &p_ssop) { // 'sessionoptions' did_set_sessionoptions(oldval, &errmsg); - } else if (varp == &p_vop) { // 'viewoptions' + } else if (varp == &p_vop) { // 'viewoptions' did_set_opt_flags(p_vop, p_ssop_values, &vop_flags, true, &errmsg); - } else if (varp == &p_rdb) { // 'redrawdebug' + } else if (varp == &p_rdb) { // 'redrawdebug' did_set_opt_flags(p_rdb, p_rdb_values, &rdb_flags, true, &errmsg); - } else if (varp == &p_sbo) { // 'scrollopt' + } else if (varp == &p_sbo) { // 'scrollopt' did_set_opt_strings(p_sbo, p_scbopt_values, true, &errmsg); - } else if (varp == &p_ambw || (int *)varp == &p_emoji) { // 'ambiwidth' + } else if (varp == &p_ambw // 'ambiwidth' + || (int *)varp == &p_emoji) { // 'emoji' did_set_ambiwidth(&errmsg); - } else if (varp == &p_bg) { // 'background' + } else if (varp == &p_bg) { // 'background' did_set_background(&errmsg); - } else if (varp == &p_wim) { // 'wildmode' + } else if (varp == &p_wim) { // 'wildmode' did_set_wildmode(&errmsg); - } else if (varp == &p_wop) { // 'wildoptions' + } else if (varp == &p_wop) { // 'wildoptions' did_set_opt_flags(p_wop, p_wop_values, &wop_flags, true, &errmsg); - } else if (varp == &p_wak) { // 'winaltkeys' + } else if (varp == &p_wak) { // 'winaltkeys' did_set_winaltkeys(&errmsg); - } else if (varp == &p_ei) { // 'eventignore' + } else if (varp == &p_ei) { // 'eventignore' did_set_eventignore(&errmsg); - } else if (varp == &p_enc || gvarp == &p_fenc || gvarp == &p_menc) { - // 'encoding', 'fileencoding' and 'makeencoding' + } else if (varp == &p_enc // 'encoding' + || gvarp == &p_fenc // 'fileencoding' + || gvarp == &p_menc) { // 'makeencoding' did_set_encoding(buf, varp, gvarp, opt_flags, &errmsg); - } else if (varp == &buf->b_p_keymap) { + } else if (varp == &buf->b_p_keymap) { // 'keymap' did_set_keymap(buf, varp, opt_flags, value_checked, &errmsg); - } else if (gvarp == &p_ff) { // 'fileformat' + } else if (gvarp == &p_ff) { // 'fileformat' did_set_fileformat(buf, varp, oldval, opt_flags, &errmsg); - } else if (varp == &p_ffs) { // 'fileformats' + } else if (varp == &p_ffs) { // 'fileformats' did_set_opt_strings(p_ffs, p_ff_values, true, &errmsg); - } else if (gvarp == &p_mps) { // 'matchpairs' + } else if (gvarp == &p_mps) { // 'matchpairs' did_set_matchpairs(varp, &errmsg); - } else if (gvarp == &p_com) { // 'comments' + } else if (gvarp == &p_com) { // 'comments' did_set_comments(varp, errbuf, errbuflen, &errmsg); - } else if (varp == &p_lcs || varp == &p_fcs) { // global 'listchars' or 'fillchars' + } else if (varp == &p_lcs // global 'listchars' + || varp == &p_fcs) { // global 'fillchars' did_set_global_listfillchars(win, varp, opt_flags, &errmsg); - } else if (varp == &win->w_p_lcs) { // local 'listchars' + } else if (varp == &win->w_p_lcs) { // local 'listchars' errmsg = set_chars_option(win, varp, true); - } else if (varp == &win->w_p_fcs) { // local 'fillchars' + } else if (varp == &win->w_p_fcs) { // local 'fillchars' errmsg = set_chars_option(win, varp, true); - } else if (varp == &p_cedit) { // 'cedit' + } else if (varp == &p_cedit) { // 'cedit' errmsg = check_cedit(); - } else if (varp == &p_vfile) { // 'verbosefile' + } else if (varp == &p_vfile) { // 'verbosefile' did_set_verbosefile(&errmsg); - } else if (varp == &p_shada) { // 'shada' + } else if (varp == &p_shada) { // 'shada' did_set_shada(&opt, &opt_idx, &free_oldval, errbuf, errbuflen, &errmsg); - } else if (gvarp == &p_sbr) { // 'showbreak' + } else if (gvarp == &p_sbr) { // 'showbreak' did_set_showbreak(varp, &errmsg); - } else if (varp == &p_guicursor) { // 'guicursor' + } else if (varp == &p_guicursor) { // 'guicursor' errmsg = parse_shape_opt(SHAPE_CURSOR); - } else if (varp == &p_langmap) { // 'langmap' + } else if (varp == &p_langmap) { // 'langmap' langmap_set(); - } else if (varp == &p_breakat) { // 'breakat' + } else if (varp == &p_breakat) { // 'breakat' fill_breakat_flags(); - } else if (varp == &p_titlestring || varp == &p_iconstring) { - // 'titlestring' and 'iconstring' + } else if (varp == &p_titlestring // 'titlestring' + || varp == &p_iconstring) { // 'iconstring' did_set_titleiconstring(varp); - } else if (varp == &p_sel) { // 'selection' + } else if (varp == &p_sel) { // 'selection' did_set_selection(&errmsg); - } else if (varp == &p_slm) { // 'selectmode' + } else if (varp == &p_slm) { // 'selectmode' did_set_opt_strings(p_slm, p_slm_values, true, &errmsg); - } else if (varp == &p_km) { // 'keymodel' + } else if (varp == &p_km) { // 'keymodel' did_set_keymodel(&errmsg); - } else if (varp == &p_mousem) { // 'mousemodel' + } else if (varp == &p_mousem) { // 'mousemodel' did_set_opt_strings(p_mousem, p_mousem_values, false, &errmsg); - } else if (varp == &p_mousescroll) { // 'mousescroll' + } else if (varp == &p_mousescroll) { // 'mousescroll' errmsg = check_mousescroll(p_mousescroll); - } else if (varp == &p_swb) { // 'switchbuf' + } else if (varp == &p_swb) { // 'switchbuf' did_set_opt_flags(p_swb, p_swb_values, &swb_flags, true, &errmsg); - } else if (varp == &p_spk) { // 'splitkeep' + } else if (varp == &p_spk) { // 'splitkeep' did_set_opt_strings(p_spk, p_spk_values, false, &errmsg); - } else if (varp == &p_debug) { // 'debug' + } else if (varp == &p_debug) { // 'debug' did_set_opt_strings(p_debug, p_debug_values, true, &errmsg); - } else if (varp == &p_dy) { // 'display' + } else if (varp == &p_dy) { // 'display' did_set_display(&errmsg); - } else if (varp == &p_ead) { // 'eadirection' + } else if (varp == &p_ead) { // 'eadirection' did_set_opt_strings(p_ead, p_ead_values, false, &errmsg); - } else if (varp == &p_cb) { // 'clipboard' + } else if (varp == &p_cb) { // 'clipboard' did_set_opt_flags(p_cb, p_cb_values, &cb_flags, true, &errmsg); - } else if (varp == &win->w_s->b_p_spf) { + } else if (varp == &win->w_s->b_p_spf) { // 'spellfile' did_set_spellfile(varp, &errmsg); - } else if (varp == &win->w_s->b_p_spl) { // 'spell' + } else if (varp == &win->w_s->b_p_spl) { // 'spell' did_set_spell(varp, &errmsg); - } else if (varp == &win->w_s->b_p_spc) { + } else if (varp == &win->w_s->b_p_spc) { // 'spellcapcheck' did_set_spellcapcheck(win, &errmsg); - } else if (varp == &win->w_s->b_p_spo) { // 'spelloptions' + } else if (varp == &win->w_s->b_p_spo) { // 'spelloptions' did_set_spelloptions(win, &errmsg); - } else if (varp == &p_sps) { // 'spellsuggest' + } else if (varp == &p_sps) { // 'spellsuggest' did_set_spellsuggest(&errmsg); - } else if (varp == &p_msm) { // 'mkspellmem' + } else if (varp == &p_msm) { // 'mkspellmem' did_set_mkspellmem(&errmsg); - } else if (gvarp == &p_bh) { + } else if (gvarp == &p_bh) { // 'bufhidden' did_set_opt_strings(buf->b_p_bh, p_bufhidden_values, false, &errmsg); - } else if (gvarp == &p_bt) { // 'buftype' + } else if (gvarp == &p_bt) { // 'buftype' did_set_buftype(buf, win, &errmsg); - } else if (gvarp == &p_stl || gvarp == &p_wbr || varp == &p_tal - || varp == &p_ruf || varp == &win->w_p_stc) { - // 'statusline', 'winbar', 'tabline', 'rulerformat' or 'statuscolumn' + } else if (gvarp == &p_stl // 'statusline' + || gvarp == &p_wbr // 'winbar' + || varp == &p_tal // 'tabline' + || varp == &p_ruf // 'rulerformat' + || varp == &win->w_p_stc) { // 'statuscolumn' did_set_statusline(win, varp, gvarp, &errmsg); - } else if (gvarp == &p_cpt) { // 'complete' + } else if (gvarp == &p_cpt) { // 'complete' did_set_complete(varp, errbuf, errbuflen, &errmsg); - } else if (varp == &p_cot) { // 'completeopt' + } else if (varp == &p_cot) { // 'completeopt' did_set_completeopt(&errmsg); #ifdef BACKSLASH_IN_FILENAME - } else if (gvarp == &p_csl) { // 'completeslash' - if (check_opt_strings(p_csl, p_csl_values, false) != OK - || check_opt_strings(buf->b_p_csl, p_csl_values, false) != OK) { - errmsg = e_invarg; - } + } else if (gvarp == &p_csl) { // 'completeslash' + did_set_completeslash(buf, &errmsg); #endif - } else if (varp == &win->w_p_scl) { // 'signcolumn' + } else if (varp == &win->w_p_scl) { // 'signcolumn' did_set_signcolumn(win, varp, oldval, &errmsg); - } else if (varp == &p_sloc) { // 'showcmdloc' + } else if (varp == &p_sloc) { // 'showcmdloc' did_set_opt_strings(*varp, p_sloc_values, false, &errmsg); - } else if (varp == &win->w_p_fdc - || varp == &win->w_allbuf_opt.wo_fdc) { - // 'foldcolumn' + } else if (gvarp == &win->w_allbuf_opt.wo_fdc) { // 'foldcolumn' did_set_foldcolumn(varp, &errmsg); - } else if (varp == &p_pt) { // 'pastetoggle' + } else if (varp == &p_pt) { // 'pastetoggle' did_set_pastetoggle(); - } else if (varp == &p_bs) { // 'backspace' + } else if (varp == &p_bs) { // 'backspace' did_set_backspace(&errmsg); } else if (varp == &p_bo) { did_set_opt_flags(p_bo, p_bo_values, &bo_flags, true, &errmsg); - } else if (gvarp == &p_tc) { // 'tagcase' + } else if (gvarp == &p_tc) { // 'tagcase' did_set_tagcase(buf, opt_flags, &errmsg); - } else if (varp == &p_cmp) { // 'casemap' + } else if (varp == &p_cmp) { // 'casemap' did_set_opt_flags(p_cmp, p_cmp_values, &cmp_flags, true, &errmsg); - } else if (varp == &p_dip) { // 'diffopt' + } else if (varp == &p_dip) { // 'diffopt' did_set_diffopt(&errmsg); - } else if (gvarp == &win->w_allbuf_opt.wo_fdm) { // 'foldmethod' + } else if (gvarp == &win->w_allbuf_opt.wo_fdm) { // 'foldmethod' did_set_foldmethod(win, varp, &errmsg); - } else if (gvarp == &win->w_allbuf_opt.wo_fmr) { // 'foldmarker' + } else if (gvarp == &win->w_allbuf_opt.wo_fmr) { // 'foldmarker' did_set_foldmarker(win, varp, &errmsg); - } else if (gvarp == &p_cms) { // 'commentstring' + } else if (gvarp == &p_cms) { // 'commentstring' did_set_commentstring(varp, &errmsg); - } else if (varp == &p_fdo) { // 'foldopen' + } else if (varp == &p_fdo) { // 'foldopen' did_set_opt_flags(p_fdo, p_fdo_values, &fdo_flags, true, &errmsg); - } else if (varp == &p_fcl) { // 'foldclose' + } else if (varp == &p_fcl) { // 'foldclose' did_set_opt_strings(*varp, p_fcl_values, true, &errmsg); - } else if (gvarp == &win->w_allbuf_opt.wo_fdi) { // 'foldignore' + } else if (gvarp == &win->w_allbuf_opt.wo_fdi) { // 'foldignore' did_set_foldignore(win); - } else if (gvarp == &p_ve) { // 'virtualedit' + } else if (gvarp == &p_ve) { // 'virtualedit' did_set_virtualedit(win, opt_flags, oldval, &errmsg); - } else if (gvarp == &p_cino) { // 'cinoptions' + } else if (gvarp == &p_cino) { // 'cinoptions' // TODO(vim): recognize errors parse_cino(buf); - } else if (gvarp == &p_lop) { // 'lispoptions' + } else if (gvarp == &p_lop) { // 'lispoptions' did_set_lispoptions(varp, &errmsg); - } else if (varp == &p_icm) { // 'inccommand' + } else if (varp == &p_icm) { // 'inccommand' did_set_opt_strings(*varp, p_icm_values, false, &errmsg); - } else if (gvarp == &p_ft || gvarp == &p_syn) { + } else if (gvarp == &p_ft // 'filetype' + || gvarp == &p_syn) { // 'syntax' did_set_filetype_or_syntax(varp, oldval, value_checked, &value_changed, &errmsg); - } else if (varp == &win->w_p_winhl) { + } else if (varp == &win->w_p_winhl) { // 'winhighlight' did_set_winhl(win, &errmsg); } else if (varp == &p_tpf) { did_set_opt_flags(p_tpf, p_tpf_values, &tpf_flags, true, &errmsg); - } else if (varp == &buf->b_p_vsts) { // 'varsofttabstop' + } else if (varp == &buf->b_p_vsts) { // 'varsofttabstop' did_set_varsoftabstop(buf, varp, &errmsg); - } else if (varp == &buf->b_p_vts) { // 'vartabstop' + } else if (varp == &buf->b_p_vts) { // 'vartabstop' did_set_vartabstop(buf, win, varp, &errmsg); - } else if (varp == &p_dex) { // 'diffexpr' - did_set_optexpr(win, &p_dex, varp, gvarp); - } else if (varp == &win->w_p_fde) { // 'foldexpr' - did_set_optexpr(win, &win->w_p_fde, varp, gvarp); - if (foldmethodIsExpr(win)) { + } else if (varp == &p_dex // 'diffexpr' + || gvarp == &win->w_allbuf_opt.wo_fde // 'foldexpr' + || gvarp == &win->w_allbuf_opt.wo_fdt // 'foldtext' + || gvarp == &p_fex // 'formatexpr' + || gvarp == &p_inex // 'includeexpr' + || gvarp == &p_inde // 'indentexpr' + || varp == &p_pex // 'patchexpr' + || varp == &p_ccv) { // 'charconvert' + did_set_optexpr(varp); + if (varp == &win->w_p_fde && foldmethodIsExpr(win)) { foldUpdateAll(win); } - } else if (varp == &win->w_p_fdt) { // 'foldtext' - did_set_optexpr(win, &win->w_p_fdt, varp, gvarp); - } else if (varp == &p_pex) { // 'patchexpr' - did_set_optexpr(win, &p_pex, varp, gvarp); - } else if (gvarp == &p_fex) { // 'formatexpr' - did_set_optexpr(win, &buf->b_p_fex, varp, gvarp); - } else if (gvarp == &p_inex) { // 'includeexpr' - did_set_optexpr(win, &buf->b_p_inex, varp, gvarp); - } else if (gvarp == &p_inde) { // 'indentexpr' - did_set_optexpr(win, &buf->b_p_inde, varp, gvarp); - } else if (gvarp == &p_cfu) { // 'completefunc' + } else if (gvarp == &p_cfu) { // 'completefunc' set_completefunc_option(&errmsg); - } else if (gvarp == &p_ofu) { // 'omnifunc' + } else if (gvarp == &p_ofu) { // 'omnifunc' set_omnifunc_option(buf, &errmsg); - } else if (gvarp == &p_tsrfu) { // 'thesaurusfunc' + } else if (gvarp == &p_tsrfu) { // 'thesaurusfunc' set_thesaurusfunc_option(&errmsg); - } else if (varp == &p_opfunc) { // 'operatorfunc' + } else if (varp == &p_opfunc) { // 'operatorfunc' set_operatorfunc_option(&errmsg); - } else if (varp == &p_qftf) { // 'quickfixtextfunc' + } else if (varp == &p_qftf) { // 'quickfixtextfunc' qf_process_qftf_option(&errmsg); - } else if (gvarp == &p_tfu) { // 'tagfunc' + } else if (gvarp == &p_tfu) { // 'tagfunc' set_tagfunc_option(&errmsg); - } else if (varp == &p_ww) { // 'whichwrap' + } else if (varp == &p_ww) { // 'whichwrap' did_set_option_listflag(varp, WW_ALL, errbuf, errbuflen, &errmsg); - } else if (varp == &p_shm) { // 'shortmess' + } else if (varp == &p_shm) { // 'shortmess' did_set_option_listflag(varp, SHM_ALL, errbuf, errbuflen, &errmsg); - } else if (varp == &p_cpo) { // 'cpoptions' + } else if (varp == &p_cpo) { // 'cpoptions' did_set_option_listflag(varp, CPO_VI, errbuf, errbuflen, &errmsg); - } else if (varp == &buf->b_p_fo) { // 'formatoptions' + } else if (varp == &buf->b_p_fo) { // 'formatoptions' did_set_option_listflag(varp, FO_ALL, errbuf, errbuflen, &errmsg); - } else if (varp == &win->w_p_cocu) { // 'concealcursor' + } else if (varp == &win->w_p_cocu) { // 'concealcursor' did_set_option_listflag(varp, COCU_ALL, errbuf, errbuflen, &errmsg); - } else if (varp == &p_mouse) { // 'mouse' + } else if (varp == &p_mouse) { // 'mouse' did_set_option_listflag(varp, MOUSE_ALL, errbuf, errbuflen, &errmsg); - } else if (gvarp == &p_flp) { + } else if (gvarp == &p_flp) { // 'formatlistpat' if (win->w_briopt_list) { // Changing Formatlistpattern when briopt includes the list setting: // redraw @@ -1857,7 +1863,7 @@ static char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx, char } } - // If error detected, restore the previous value. + // If an error is detected, restore the previous value. if (errmsg != NULL) { free_string_option(*varp); *varp = oldval; @@ -1894,7 +1900,7 @@ static char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx, char } else if (varp == &buf->b_p_ft) { do_filetype_autocmd(buf, varp, opt_flags, value_changed); } else if (varp == &win->w_s->b_p_spl) { - did_set_spelllang_source(win); + do_spelllang_source(win); } } diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 302faa8140..6157341ec9 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -146,11 +146,7 @@ bool os_isdir(const char *name) return false; } - if (!S_ISDIR(mode)) { - return false; - } - - return true; + return S_ISDIR(mode); } /// Check what `name` is: diff --git a/src/nvim/path.c b/src/nvim/path.c index 513f366a27..e4c2253357 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1138,7 +1138,7 @@ static int expand_in_path(garray_T *const gap, char *const pattern, const int fl if (flags & EW_ADDSLASH) { glob_flags |= WILD_ADD_SLASH; } - globpath(paths, pattern, gap, glob_flags); + globpath(paths, pattern, gap, glob_flags, false); xfree(paths); return gap->ga_len; diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c index 1b32447d77..af3d93f7c4 100644 --- a/src/nvim/regexp_bt.c +++ b/src/nvim/regexp_bt.c @@ -2862,7 +2862,7 @@ static regprog_T *bt_regcomp(uint8_t *expr, int re_flags) } // Allocate space. - bt_regprog_T *r = xmalloc(sizeof(bt_regprog_T) + (size_t)regsize); + bt_regprog_T *r = xmalloc(offsetof(bt_regprog_T, program) + (size_t)regsize); r->re_in_use = false; // Second pass: emit code. diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 93b03f0632..ea59e7d464 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -7511,7 +7511,7 @@ static regprog_T *nfa_regcomp(uint8_t *expr, int re_flags) post2nfa(postfix, post_ptr, true); // allocate the regprog with space for the compiled regexp - size_t prog_size = sizeof(nfa_regprog_T) + sizeof(nfa_state_T) * (size_t)(nstate - 1); + size_t prog_size = offsetof(nfa_regprog_T, state) + sizeof(nfa_state_T) * (size_t)nstate; prog = xmalloc(prog_size); state_ptr = prog->state; prog->re_in_use = false; diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 24500b80b9..b071e10cf9 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -26,6 +26,7 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext.h" #include "nvim/globals.h" @@ -208,29 +209,56 @@ void runtime_init(void) uv_mutex_init(&runtime_search_path_mutex); } -/// ":runtime [what] {name}" +/// Get DIP_ flags from the [where] argument of a :runtime command. +/// "*argp" is advanced to after the [where] argument. +static int get_runtime_cmd_flags(char **argp, size_t where_len) +{ + char *arg = *argp; + + if (where_len == 0) { + return 0; + } + + if (strncmp(arg, "START", where_len) == 0) { + *argp = skipwhite(arg + where_len); + return DIP_START + DIP_NORTP; + } + if (strncmp(arg, "OPT", where_len) == 0) { + *argp = skipwhite(arg + where_len); + return DIP_OPT + DIP_NORTP; + } + if (strncmp(arg, "PACK", where_len) == 0) { + *argp = skipwhite(arg + where_len); + return DIP_START + DIP_OPT + DIP_NORTP; + } + if (strncmp(arg, "ALL", where_len) == 0) { + *argp = skipwhite(arg + where_len); + return DIP_START + DIP_OPT; + } + + return 0; +} + +/// ":runtime [where] {name}" void ex_runtime(exarg_T *eap) { char *arg = eap->arg; - char *p = skiptowhite(arg); - size_t len = (size_t)(p - arg); int flags = eap->forceit ? DIP_ALL : 0; + char *p = skiptowhite(arg); + flags += get_runtime_cmd_flags(&arg, (size_t)(p - arg)); + source_runtime(arg, flags); +} - if (strncmp(arg, "START", len) == 0) { - flags += DIP_START + DIP_NORTP; - arg = skipwhite(arg + len); - } else if (strncmp(arg, "OPT", len) == 0) { - flags += DIP_OPT + DIP_NORTP; - arg = skipwhite(arg + len); - } else if (strncmp(arg, "PACK", len) == 0) { - flags += DIP_START + DIP_OPT + DIP_NORTP; - arg = skipwhite(arg + len); - } else if (strncmp(arg, "ALL", len) == 0) { - flags += DIP_START + DIP_OPT; - arg = skipwhite(arg + len); - } +static int runtime_expand_flags; - source_runtime(arg, flags); +/// Set the completion context for the :runtime command. +void set_context_in_runtime_cmd(expand_T *xp, const char *arg) +{ + char *p = skiptowhite(arg); + runtime_expand_flags + = *p != NUL ? get_runtime_cmd_flags((char **)&arg, (size_t)(p - arg)) : 0; + xp->xp_context = EXPAND_RUNTIME; + xp->xp_pattern = (char *)arg; } static void source_callback(char *fname, void *cookie) @@ -1175,102 +1203,154 @@ void ex_packadd(exarg_T *eap) } } -/// Expand color scheme, compiler or filetype names. -/// Search from 'runtimepath': -/// 'runtimepath'/{dirnames}/{pat}.(vim|lua) -/// When "flags" has DIP_START: search also from "start" of 'packpath': -/// 'packpath'/pack/*/start/*/{dirnames}/{pat}.(vim|lua) -/// When "flags" has DIP_OPT: search also from "opt" of 'packpath': -/// 'packpath'/pack/*/opt/*/{dirnames}/{pat}.(vim|lua) -/// "dirnames" is an array with one or more directory names. -int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirnames[]) +static void ExpandRTDir_int(char *pat, size_t pat_len, int flags, bool keep_ext, garray_T *gap, + char *dirnames[]) { - *num_file = 0; - *file = NULL; - size_t pat_len = strlen(pat); - - garray_T ga; - ga_init(&ga, (int)sizeof(char *), 10); - // TODO(bfredl): this is bullshit, expandpath should not reinvent path logic. for (int i = 0; dirnames[i] != NULL; i++) { - size_t size = strlen(dirnames[i]) + pat_len + 16; - char *s = xmalloc(size); - snprintf(s, size, "%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); - globpath(p_rtp, s, &ga, 0); - xfree(s); - } + const size_t buf_len = strlen(dirnames[i]) + pat_len + 31; + char *const buf = xmalloc(buf_len); + char *const tail = buf + 15; + const size_t tail_buflen = buf_len - 15; + int glob_flags = 0; + bool expand_dirs = false; + + if (*dirnames[i] == NUL) { // empty dir used for :runtime + snprintf(tail, tail_buflen, "%s*.\\(vim\\|lua\\)", pat); + } else { + snprintf(tail, tail_buflen, "%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); + } - if (flags & DIP_START) { - for (int i = 0; dirnames[i] != NULL; i++) { - size_t size = strlen(dirnames[i]) + pat_len + 31; - char *s = xmalloc(size); - snprintf(s, size, "pack/*/start/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT - globpath(p_pp, s, &ga, 0); - xfree(s); +expand: + if ((flags & DIP_NORTP) == 0) { + globpath(p_rtp, tail, gap, glob_flags, expand_dirs); } - for (int i = 0; dirnames[i] != NULL; i++) { - size_t size = strlen(dirnames[i]) + pat_len + 31; - char *s = xmalloc(size); - snprintf(s, size, "start/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT - globpath(p_pp, s, &ga, 0); - xfree(s); + if (flags & DIP_START) { + memcpy(tail - 15, "pack/*/start/*/", 15); // NOLINT + globpath(p_pp, tail - 15, gap, glob_flags, expand_dirs); + memcpy(tail - 8, "start/*/", 8); // NOLINT + globpath(p_pp, tail - 8, gap, glob_flags, expand_dirs); } - } - if (flags & DIP_OPT) { - for (int i = 0; dirnames[i] != NULL; i++) { - size_t size = strlen(dirnames[i]) + pat_len + 29; - char *s = xmalloc(size); - snprintf(s, size, "pack/*/opt/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT - globpath(p_pp, s, &ga, 0); - xfree(s); + if (flags & DIP_OPT) { + memcpy(tail - 13, "pack/*/opt/*/", 13); // NOLINT + globpath(p_pp, tail - 13, gap, glob_flags, expand_dirs); + memcpy(tail - 6, "opt/*/", 6); // NOLINT + globpath(p_pp, tail - 6, gap, glob_flags, expand_dirs); } - for (int i = 0; dirnames[i] != NULL; i++) { - size_t size = strlen(dirnames[i]) + pat_len + 29; - char *s = xmalloc(size); - snprintf(s, size, "opt/*/%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat); // NOLINT - globpath(p_pp, s, &ga, 0); - xfree(s); + if (*dirnames[i] == NUL && !expand_dirs) { + // expand dir names in another round + snprintf(tail, tail_buflen, "%s*", pat); + glob_flags = WILD_ADD_SLASH; + expand_dirs = true; + goto expand; } + + xfree(buf); } - for (int i = 0; i < ga.ga_len; i++) { - char *match = ((char **)ga.ga_data)[i]; + int pat_pathsep_cnt = 0; + for (size_t i = 0; i < pat_len; i++) { + if (vim_ispathsep(pat[i])) { + pat_pathsep_cnt++; + } + } + + for (int i = 0; i < gap->ga_len; i++) { + char *match = ((char **)gap->ga_data)[i]; char *s = match; char *e = s + strlen(s); - if (e - s > 4 && (STRNICMP(e - 4, ".vim", 4) == 0 - || STRNICMP(e - 4, ".lua", 4) == 0)) { + if (e - s > 4 && !keep_ext && (STRNICMP(e - 4, ".vim", 4) == 0 + || STRNICMP(e - 4, ".lua", 4) == 0)) { e -= 4; - for (s = e; s > match; MB_PTR_BACK(match, s)) { - if (vim_ispathsep(*s)) { - break; - } - } - s++; *e = NUL; + } + + int match_pathsep_cnt = (e > s && e[-1] == '/') ? -1 : 0; + for (s = e; s > match; MB_PTR_BACK(match, s)) { + if (vim_ispathsep(*s) && ++match_pathsep_cnt > pat_pathsep_cnt) { + break; + } + } + s++; + if (s != match) { assert((e - s) + 1 >= 0); memmove(match, s, (size_t)(e - s) + 1); } } - if (GA_EMPTY(&ga)) { - return FAIL; + if (GA_EMPTY(gap)) { + return; } // Sort and remove duplicates which can happen when specifying multiple // directories in dirnames. - ga_remove_duplicate_strings(&ga); + ga_remove_duplicate_strings(gap); +} + +/// Expand color scheme, compiler or filetype names. +/// Search from 'runtimepath': +/// 'runtimepath'/{dirnames}/{pat}.(vim|lua) +/// When "flags" has DIP_START: search also from "start" of 'packpath': +/// 'packpath'/pack/*/start/*/{dirnames}/{pat}.(vim|lua) +/// When "flags" has DIP_OPT: search also from "opt" of 'packpath': +/// 'packpath'/pack/*/opt/*/{dirnames}/{pat}.(vim|lua) +/// "dirnames" is an array with one or more directory names. +int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirnames[]) +{ + *num_file = 0; + *file = NULL; + + garray_T ga; + ga_init(&ga, (int)sizeof(char *), 10); + + ExpandRTDir_int(pat, strlen(pat), flags, false, &ga, dirnames); + + if (GA_EMPTY(&ga)) { + return FAIL; + } *file = ga.ga_data; *num_file = ga.ga_len; return OK; } +/// Handle command line completion for :runtime command. +int expand_runtime_cmd(char *pat, int *numMatches, char ***matches) +{ + *numMatches = 0; + *matches = NULL; + + garray_T ga; + ga_init(&ga, sizeof(char *), 10); + + const size_t pat_len = strlen(pat); + char *dirnames[] = { "", NULL }; + ExpandRTDir_int(pat, pat_len, runtime_expand_flags, true, &ga, dirnames); + + // Try to complete values for [where] argument when none was found. + if (runtime_expand_flags == 0) { + char *where_values[] = { "START", "OPT", "PACK", "ALL" }; + for (size_t i = 0; i < ARRAY_SIZE(where_values); i++) { + if (strncmp(pat, where_values[i], pat_len) == 0) { + GA_APPEND(char *, &ga, xstrdup(where_values[i])); + } + } + } + + if (GA_EMPTY(&ga)) { + return FAIL; + } + + *matches = ga.ga_data; + *numMatches = ga.ga_len; + return OK; +} + /// Expand loadplugin names: -/// 'packpath'/pack/ * /opt/{pat} +/// 'packpath'/pack/*/opt/{pat} int ExpandPackAddDir(char *pat, int *num_file, char ***file) { garray_T ga; @@ -1283,9 +1363,9 @@ int ExpandPackAddDir(char *pat, int *num_file, char ***file) size_t buflen = pat_len + 26; char *s = xmalloc(buflen); snprintf(s, buflen, "pack/*/opt/%s*", pat); // NOLINT - globpath(p_pp, s, &ga, 0); + globpath(p_pp, s, &ga, 0, true); snprintf(s, buflen, "opt/%s*", pat); // NOLINT - globpath(p_pp, s, &ga, 0); + globpath(p_pp, s, &ga, 0, true); xfree(s); for (int i = 0; i < ga.ga_len; i++) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 2002f554d4..f35912849d 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -768,7 +768,6 @@ void comp_col(void) /// Otherwise it depends on 'numberwidth' and the line count. int number_width(win_T *wp) { - int n; linenr_T lnum; if (wp->w_p_rnu && !wp->w_p_nu) { @@ -784,17 +783,13 @@ int number_width(win_T *wp) } wp->w_nrwidth_line_count = lnum; - // make best estimate for 'statuscolumn' + // reset for 'statuscolumn' if (*wp->w_p_stc != NUL) { - char buf[MAXPATHL]; - wp->w_nrwidth_width = 0; - n = build_statuscol_str(wp, lnum, 0, 0, NUL, buf, NULL, NULL); - n = MAX(n, (wp->w_p_nu || wp->w_p_rnu) * (int)wp->w_p_nuw); - wp->w_nrwidth_width = MIN(n, MAX_NUMBERWIDTH); + wp->w_nrwidth_width = (wp->w_p_nu || wp->w_p_rnu) * (int)wp->w_p_nuw; return wp->w_nrwidth_width; } - n = 0; + int n = 0; do { lnum /= 10; n++; diff --git a/src/nvim/search.c b/src/nvim/search.c index b24b6ad27c..eb5cc2e07f 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -2644,7 +2644,12 @@ static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool sh len += 2; } - memmove(msgbuf + strlen(msgbuf) - len, t, len); + size_t msgbuf_len = strlen(msgbuf); + if (len > msgbuf_len) { + len = msgbuf_len; + } + memmove(msgbuf + msgbuf_len - len, t, len); + if (dirc == '?' && stat.cur == maxcount + 1) { stat.cur = -1; } diff --git a/src/nvim/sign.c b/src/nvim/sign.c index d0c093d93a..00e282b76e 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -103,7 +103,7 @@ static signgroup_T *sign_group_ref(const char *groupname) hi = hash_lookup(&sg_table, (char *)groupname, strlen(groupname), hash); if (HASHITEM_EMPTY(hi)) { // new group - group = xmalloc(sizeof(signgroup_T) + strlen(groupname)); + group = xmalloc(offsetof(signgroup_T, sg_name) + strlen(groupname) + 1); STRCPY(group->sg_name, groupname); group->sg_refcount = 1; diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index 16e783aab7..7aa06ce48a 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -10,9 +10,9 @@ // Sign group typedef struct signgroup_S { - uint16_t sg_refcount; // number of signs in this group - int sg_next_sign_id; // next sign id for this group - char sg_name[1]; // sign group name + int sg_next_sign_id; ///< next sign id for this group + uint16_t sg_refcount; ///< number of signs in this group + char sg_name[1]; ///< sign group name, actually longer } signgroup_T; // Macros to get the sign group structure from the group name diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 8e18be5bd1..2204cda169 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1745,7 +1745,7 @@ void count_common_word(slang_T *lp, char *word, int len, uint8_t count) const size_t p_len = strlen(p); hashitem_T *hi = hash_lookup(&lp->sl_wordcount, (const char *)p, p_len, hash); if (HASHITEM_EMPTY(hi)) { - wc = xmalloc(sizeof(wordcount_T) + p_len); + wc = xmalloc(offsetof(wordcount_T, wc_word) + p_len + 1); memcpy(wc->wc_word, p, p_len + 1); wc->wc_count = count; hash_add_item(&lp->sl_wordcount, hi, (char *)wc->wc_word, hash); diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 44414ca1a5..7b124ae6b6 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -3855,7 +3855,7 @@ static void *getroom(spellinfo_T *spin, size_t len, bool align) if (bl == NULL || (size_t)bl->sb_used + len > SBLOCKSIZE) { // Allocate a block of memory. It is not freed until much later. - bl = xcalloc(1, (sizeof(sblock_T) + SBLOCKSIZE)); + bl = xcalloc(1, offsetof(sblock_T, sb_data) + SBLOCKSIZE + 1); bl->sb_next = spin->si_blocks; spin->si_blocks = bl; bl->sb_used = 0; diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index 22add418a0..54b6f552b5 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -2828,7 +2828,7 @@ static void add_sound_suggest(suginfo_T *su, char *goodword, int score, langp_T hi = hash_lookup(&slang->sl_sounddone, (const char *)goodword, goodword_len, hash); if (HASHITEM_EMPTY(hi)) { - sft = xmalloc(sizeof(sftword_T) + goodword_len); + sft = xmalloc(offsetof(sftword_T, sft_word) + goodword_len + 1); sft->sft_score = (int16_t)score; memcpy(sft->sft_word, goodword, goodword_len + 1); hash_add_item(&slang->sl_sounddone, hi, (char *)sft->sft_word, hash); diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 05c570e52f..49b63ad324 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -3736,7 +3736,7 @@ static void add_keyword(char *const name, const int id, const int flags, sizeof(name_folded)) : name; - keyentry_T *const kp = xmalloc(sizeof(keyentry_T) + strlen(name_ic)); + keyentry_T *const kp = xmalloc(offsetof(keyentry_T, keyword) + strlen(name_ic) + 1); STRCPY(kp->keyword, name_ic); kp->k_syn.id = (int16_t)id; kp->k_syn.inc_tag = current_syn_inc_tag; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 197184c181..42618e8924 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2732,55 +2732,57 @@ static int parse_match(char *lbuf, tagptrs_T *tagp) tagp->tagline = 0; tagp->command_end = NULL; - if (retval == OK) { - // Try to find a kind field: "kind:<kind>" or just "<kind>" - p = tagp->command; - if (find_extra(&p) == OK) { - tagp->command_end = p; - if (p > tagp->command && p[-1] == '|') { - tagp->command_end = p - 1; // drop trailing bar - } - p += 2; // skip ";\"" - if (*p++ == TAB) { - // Accept ASCII alphabetic kind characters and any multi-byte - // character. - while (ASCII_ISALPHA(*p) || utfc_ptr2len(p) > 1) { - if (strncmp(p, "kind:", 5) == 0) { - tagp->tagkind = p + 5; - } else if (strncmp(p, "user_data:", 10) == 0) { - tagp->user_data = p + 10; - } else if (strncmp(p, "line:", 5) == 0) { - tagp->tagline = atoi(p + 5); - } - if (tagp->tagkind != NULL && tagp->user_data != NULL) { - break; - } + if (retval != OK) { + return retval; + } - pc = vim_strchr(p, ':'); - pt = vim_strchr(p, '\t'); - if (pc == NULL || (pt != NULL && pc > pt)) { - tagp->tagkind = p; - } - if (pt == NULL) { - break; - } - p = pt; - MB_PTR_ADV(p); + // Try to find a kind field: "kind:<kind>" or just "<kind>" + p = tagp->command; + if (find_extra(&p) == OK) { + tagp->command_end = p; + if (p > tagp->command && p[-1] == '|') { + tagp->command_end = p - 1; // drop trailing bar + } + p += 2; // skip ";\"" + if (*p++ == TAB) { + // Accept ASCII alphabetic kind characters and any multi-byte + // character. + while (ASCII_ISALPHA(*p) || utfc_ptr2len(p) > 1) { + if (strncmp(p, "kind:", 5) == 0) { + tagp->tagkind = p + 5; + } else if (strncmp(p, "user_data:", 10) == 0) { + tagp->user_data = p + 10; + } else if (strncmp(p, "line:", 5) == 0) { + tagp->tagline = atoi(p + 5); } + if (tagp->tagkind != NULL && tagp->user_data != NULL) { + break; + } + + pc = vim_strchr(p, ':'); + pt = vim_strchr(p, '\t'); + if (pc == NULL || (pt != NULL && pc > pt)) { + tagp->tagkind = p; + } + if (pt == NULL) { + break; + } + p = pt; + MB_PTR_ADV(p); } } - if (tagp->tagkind != NULL) { - for (p = tagp->tagkind; - *p && *p != '\t' && *p != '\r' && *p != '\n'; - MB_PTR_ADV(p)) {} - tagp->tagkind_end = p; - } - if (tagp->user_data != NULL) { - for (p = tagp->user_data; - *p && *p != '\t' && *p != '\r' && *p != '\n'; - MB_PTR_ADV(p)) {} - tagp->user_data_end = p; - } + } + if (tagp->tagkind != NULL) { + for (p = tagp->tagkind; + *p && *p != '\t' && *p != '\r' && *p != '\n'; + MB_PTR_ADV(p)) {} + tagp->tagkind_end = p; + } + if (tagp->user_data != NULL) { + for (p = tagp->user_data; + *p && *p != '\t' && *p != '\r' && *p != '\n'; + MB_PTR_ADV(p)) {} + tagp->user_data_end = p; } return retval; } @@ -3329,86 +3331,88 @@ int get_tags(list_T *list, char *pat, char *buf_fname) ret = find_tags(pat, &num_matches, &matches, TAG_REGEXP | TAG_NOIC, MAXCOL, buf_fname); - if (ret == OK && num_matches > 0) { - for (i = 0; i < num_matches; i++) { - int parse_result = parse_match(matches[i], &tp); + if (ret != OK || num_matches <= 0) { + return ret; + } - // Avoid an unused variable warning in release builds. - (void)parse_result; - assert(parse_result == OK); + for (i = 0; i < num_matches; i++) { + int parse_result = parse_match(matches[i], &tp); - is_static = test_for_static(&tp); + // Avoid an unused variable warning in release builds. + (void)parse_result; + assert(parse_result == OK); - // Skip pseudo-tag lines. - if (strncmp(tp.tagname, "!_TAG_", 6) == 0) { - xfree(matches[i]); - continue; - } + is_static = test_for_static(&tp); + + // Skip pseudo-tag lines. + if (strncmp(tp.tagname, "!_TAG_", 6) == 0) { + xfree(matches[i]); + continue; + } - 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, "kind", tp.tagkind, - tp.tagkind ? tp.tagkind_end : NULL) == FAIL - || tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) { - ret = FAIL; - } - - xfree(full_fname); - - if (tp.command_end != NULL) { - for (char *p = tp.command_end + 3; - *p != NUL && *p != '\n' && *p != '\r'; - MB_PTR_ADV(p)) { - if (p == tp.tagkind - || (p + 5 == tp.tagkind && strncmp(p, "kind:", 5) == 0)) { - // skip "kind:<kind>" and "<kind>" - p = tp.tagkind_end - 1; - } else if (strncmp(p, "file:", 5) == 0) { - // skip "file:" (static tag) - p += 4; - } else if (!ascii_iswhite(*p)) { - char *s, *n; - int len; - - // Add extra field as a dict entry. Fields are - // separated by Tabs. - n = p; - while (*p != NUL && *p >= ' ' && *p < 127 && *p != ':') { + 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, "kind", tp.tagkind, + tp.tagkind ? tp.tagkind_end : NULL) == FAIL + || tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) { + ret = FAIL; + } + + xfree(full_fname); + + if (tp.command_end != NULL) { + for (char *p = tp.command_end + 3; + *p != NUL && *p != '\n' && *p != '\r'; + MB_PTR_ADV(p)) { + if (p == tp.tagkind + || (p + 5 == tp.tagkind && strncmp(p, "kind:", 5) == 0)) { + // skip "kind:<kind>" and "<kind>" + p = tp.tagkind_end - 1; + } else if (strncmp(p, "file:", 5) == 0) { + // skip "file:" (static tag) + p += 4; + } else if (!ascii_iswhite(*p)) { + char *s, *n; + int len; + + // Add extra field as a dict entry. Fields are + // separated by Tabs. + n = p; + while (*p != NUL && *p >= ' ' && *p < 127 && *p != ':') { + p++; + } + len = (int)(p - n); + if (*p == ':' && len > 0) { + s = ++p; + while (*p != NUL && (uint8_t)(*p) >= ' ') { p++; } - len = (int)(p - n); - if (*p == ':' && len > 0) { - s = ++p; - while (*p != NUL && (uint8_t)(*p) >= ' ') { - p++; - } - n[len] = NUL; - if (add_tag_field(dict, n, s, p) == FAIL) { - ret = FAIL; - } - n[len] = ':'; - } else { - // Skip field without colon. - while (*p != NUL && (uint8_t)(*p) >= ' ') { - p++; - } + n[len] = NUL; + if (add_tag_field(dict, n, s, p) == FAIL) { + ret = FAIL; } - if (*p == NUL) { - break; + n[len] = ':'; + } else { + // Skip field without colon. + while (*p != NUL && (uint8_t)(*p) >= ' ') { + p++; } } + if (*p == NUL) { + break; + } } } - - xfree(matches[i]); } - xfree(matches); + + xfree(matches[i]); } + xfree(matches); return ret; } diff --git a/src/nvim/testdir/runnvim.sh b/src/nvim/testdir/runnvim.sh index 322265737a..3a0a94b6bf 100755 --- a/src/nvim/testdir/runnvim.sh +++ b/src/nvim/testdir/runnvim.sh @@ -30,6 +30,9 @@ main() {( . "$CI_DIR/common/suite.sh" . "$CI_DIR/common/test.sh" + # Redirect XDG_CONFIG_HOME so users local config doesn't interfere + export XDG_CONFIG_HOME="$root" + export VIMRUNTIME="$root/runtime" if ! "$nvim_prg" \ -u NONE -i NONE \ diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index fd54f77ccb..89a9179e60 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -336,8 +336,23 @@ func Test_edit_11_indentexpr() endfunc set indentexpr=s:NewIndentExpr() call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &indentexpr) + call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &g:indentexpr) set indentexpr=<SID>NewIndentExpr() call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &indentexpr) + call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &g:indentexpr) + setlocal indentexpr= + setglobal indentexpr=s:NewIndentExpr() + call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &g:indentexpr) + call assert_equal('', &indentexpr) + new + call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &indentexpr) + bw! + setglobal indentexpr=<SID>NewIndentExpr() + call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &g:indentexpr) + call assert_equal('', &indentexpr) + new + call assert_equal(expand('<SID>') .. 'NewIndentExpr()', &indentexpr) + bw! set indentexpr& bw! diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index cddb1349f5..7b5ec22dd4 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -123,7 +123,7 @@ let s:filename_checks = { \ 'conaryrecipe': ['file.recipe'], \ 'conf': ['auto.master'], \ 'config': ['configure.in', 'configure.ac', '/etc/hostname.file', 'any/etc/hostname.file'], - \ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf', 'any/.aws/config', 'any/.aws/credentials'], + \ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf', 'any/.aws/config', 'any/.aws/credentials', 'file.nmconnection'], \ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'], \ 'cook': ['file.cook'], \ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh'], @@ -181,6 +181,7 @@ let s:filename_checks = { \ 'elixir': ['file.ex', 'file.exs', 'mix.lock'], \ 'elm': ['file.elm'], \ 'elmfilt': ['filter-rules'], + \ 'elsa': ['file.lc'], \ 'elvish': ['file.elv'], \ 'epuppet': ['file.epp'], \ 'erlang': ['file.erl', 'file.hrl', 'file.yaws'], @@ -198,6 +199,7 @@ let s:filename_checks = { \ 'fennel': ['file.fnl'], \ 'fetchmail': ['.fetchmailrc'], \ 'fgl': ['file.4gl', 'file.4gh', 'file.m4gl'], + \ 'firrtl': ['file.fir'], \ 'fish': ['file.fish'], \ 'focexec': ['file.fex', 'file.focexec'], \ 'form': ['file.frm'], @@ -294,13 +296,14 @@ let s:filename_checks = { \ 'jq': ['file.jq'], \ 'jovial': ['file.jov', 'file.j73', 'file.jovial'], \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file', 'org.eclipse.xyz.prefs'], - \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.prettierrc', '.firebaserc', 'file.slnf'], + \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.prettierrc', '.firebaserc', '.stylelintrc', 'file.slnf'], \ 'json5': ['file.json5'], \ 'jsonc': ['file.jsonc', '.babelrc', '.eslintrc', '.jsfmtrc', '.jshintrc', '.hintrc', '.swrc', 'jsconfig.json', 'tsconfig.json', 'tsconfig.test.json', 'tsconfig-test.json'], \ 'jsonnet': ['file.jsonnet', 'file.libsonnet'], \ 'jsp': ['file.jsp'], \ 'julia': ['file.jl'], \ 'kconfig': ['Kconfig', 'Kconfig.debug', 'Kconfig.file'], + \ 'kdl': ['file.kdl'], \ 'kivy': ['file.kv'], \ 'kix': ['file.kix'], \ 'kotlin': ['file.kt', 'file.ktm', 'file.kts'], @@ -655,7 +658,7 @@ let s:filename_checks = { \ 'xsd': ['file.xsd'], \ 'xslt': ['file.xsl', 'file.xslt'], \ 'yacc': ['file.yy', 'file.yxx', 'file.y++'], - \ 'yaml': ['file.yaml', 'file.yml', '.clang-format', '.clang-tidy'], + \ 'yaml': ['file.yaml', 'file.yml', '.clangd', '.clang-format', '.clang-tidy'], \ 'yang': ['file.yang'], \ 'z8a': ['file.z8a'], \ 'zig': ['file.zig'], diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 19415286ad..9014948fb4 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -1305,6 +1305,7 @@ func Test_foldexpr_scriptlocal_func() set foldmethod=expr foldexpr=s:FoldFunc() redraw! call assert_equal(expand('<SID>') .. 'FoldFunc()', &foldexpr) + call assert_equal(expand('<SID>') .. 'FoldFunc()', &g:foldexpr) call assert_equal(1, g:FoldLnum) set foldmethod& foldexpr= bw! @@ -1314,8 +1315,31 @@ func Test_foldexpr_scriptlocal_func() set foldmethod=expr foldexpr=<SID>FoldFunc() redraw! call assert_equal(expand('<SID>') .. 'FoldFunc()', &foldexpr) + call assert_equal(expand('<SID>') .. 'FoldFunc()', &g:foldexpr) call assert_equal(1, g:FoldLnum) - set foldmethod& foldexpr= + bw! + call setline(1, 'abc') + setlocal foldmethod& foldexpr& + setglobal foldmethod=expr foldexpr=s:FoldFunc() + call assert_equal(expand('<SID>') .. 'FoldFunc()', &g:foldexpr) + call assert_equal('0', &foldexpr) + enew! + call setline(1, 'abc') + redraw! + call assert_equal(expand('<SID>') .. 'FoldFunc()', &foldexpr) + call assert_equal(1, g:FoldLnum) + bw! + call setline(1, 'abc') + setlocal foldmethod& foldexpr& + setglobal foldmethod=expr foldexpr=<SID>FoldFunc() + call assert_equal(expand('<SID>') .. 'FoldFunc()', &g:foldexpr) + call assert_equal('0', &foldexpr) + enew! + call setline(1, 'abc') + redraw! + call assert_equal(expand('<SID>') .. 'FoldFunc()', &foldexpr) + call assert_equal(1, g:FoldLnum) + set foldmethod& foldexpr& delfunc s:FoldFunc bw! endfunc @@ -1329,25 +1353,53 @@ func Test_foldtext_scriptlocal_func() new | only call setline(1, range(50)) let g:FoldTextArgs = [] - set foldmethod=manual set foldtext=s:FoldText() norm! 4Gzf4j redraw! call assert_equal(expand('<SID>') .. 'FoldText()', &foldtext) + call assert_equal(expand('<SID>') .. 'FoldText()', &g:foldtext) call assert_equal([4, 8], g:FoldTextArgs) set foldtext& bw! new | only call setline(1, range(50)) let g:FoldTextArgs = [] - set foldmethod=manual set foldtext=<SID>FoldText() norm! 8Gzf4j redraw! call assert_equal(expand('<SID>') .. 'FoldText()', &foldtext) + call assert_equal(expand('<SID>') .. 'FoldText()', &g:foldtext) call assert_equal([8, 12], g:FoldTextArgs) set foldtext& bw! + call setline(1, range(50)) + let g:FoldTextArgs = [] + setlocal foldtext& + setglobal foldtext=s:FoldText() + call assert_equal(expand('<SID>') .. 'FoldText()', &g:foldtext) + call assert_equal('foldtext()', &foldtext) + enew! + call setline(1, range(50)) + norm! 12Gzf4j + redraw! + call assert_equal(expand('<SID>') .. 'FoldText()', &foldtext) + call assert_equal([12, 16], g:FoldTextArgs) + set foldtext& + bw! + call setline(1, range(50)) + let g:FoldTextArgs = [] + setlocal foldtext& + setglobal foldtext=<SID>FoldText() + call assert_equal(expand('<SID>') .. 'FoldText()', &g:foldtext) + call assert_equal('foldtext()', &foldtext) + enew! + call setline(1, range(50)) + norm! 16Gzf4j + redraw! + call assert_equal(expand('<SID>') .. 'FoldText()', &foldtext) + call assert_equal([16, 20], g:FoldTextArgs) + set foldtext& + bw! delfunc s:FoldText endfunc diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim index e369645328..f09dbd72ce 100644 --- a/src/nvim/testdir/test_gf.vim +++ b/src/nvim/testdir/test_gf.vim @@ -234,6 +234,7 @@ func Test_includeexpr_scriptlocal_func() endfunc set includeexpr=s:IncludeFunc() call assert_equal(expand('<SID>') .. 'IncludeFunc()', &includeexpr) + call assert_equal(expand('<SID>') .. 'IncludeFunc()', &g:includeexpr) new | only call setline(1, 'TestFile1') let g:IncludeFname = '' @@ -242,11 +243,35 @@ func Test_includeexpr_scriptlocal_func() bw! set includeexpr=<SID>IncludeFunc() call assert_equal(expand('<SID>') .. 'IncludeFunc()', &includeexpr) + call assert_equal(expand('<SID>') .. 'IncludeFunc()', &g:includeexpr) new | only call setline(1, 'TestFile2') let g:IncludeFname = '' call assert_fails('normal! gf', 'E447:') call assert_equal('TestFile2', g:IncludeFname) + bw! + setlocal includeexpr= + setglobal includeexpr=s:IncludeFunc() + call assert_equal(expand('<SID>') .. 'IncludeFunc()', &g:includeexpr) + call assert_equal('', &includeexpr) + new + call assert_equal(expand('<SID>') .. 'IncludeFunc()', &includeexpr) + call setline(1, 'TestFile3') + let g:IncludeFname = '' + call assert_fails('normal! gf', 'E447:') + call assert_equal('TestFile3', g:IncludeFname) + bw! + setlocal includeexpr= + setglobal includeexpr=<SID>IncludeFunc() + call assert_equal(expand('<SID>') .. 'IncludeFunc()', &g:includeexpr) + call assert_equal('', &includeexpr) + new + call assert_equal(expand('<SID>') .. 'IncludeFunc()', &includeexpr) + call setline(1, 'TestFile4') + let g:IncludeFname = '' + call assert_fails('normal! gf', 'E447:') + call assert_equal('TestFile4', g:IncludeFname) + bw! set includeexpr& delfunc s:IncludeFunc bw! diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 7c90b444e5..2aaa1ff830 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -262,6 +262,7 @@ func Test_formatexpr_scriptlocal_func() endfunc set formatexpr=s:Format() call assert_equal(expand('<SID>') .. 'Format()', &formatexpr) + call assert_equal(expand('<SID>') .. 'Format()', &g:formatexpr) new | only call setline(1, range(1, 40)) let g:FormatArgs = [] @@ -270,6 +271,7 @@ func Test_formatexpr_scriptlocal_func() bw! set formatexpr=<SID>Format() call assert_equal(expand('<SID>') .. 'Format()', &formatexpr) + call assert_equal(expand('<SID>') .. 'Format()', &g:formatexpr) new | only call setline(1, range(1, 40)) let g:FormatArgs = [] @@ -277,6 +279,7 @@ func Test_formatexpr_scriptlocal_func() call assert_equal([4, 2], g:FormatArgs) bw! let &formatexpr = 's:Format()' + call assert_equal(expand('<SID>') .. 'Format()', &g:formatexpr) new | only call setline(1, range(1, 40)) let g:FormatArgs = [] @@ -284,12 +287,55 @@ func Test_formatexpr_scriptlocal_func() call assert_equal([6, 2], g:FormatArgs) bw! let &formatexpr = '<SID>Format()' + call assert_equal(expand('<SID>') .. 'Format()', &g:formatexpr) new | only call setline(1, range(1, 40)) let g:FormatArgs = [] normal! 8GVjgq call assert_equal([8, 2], g:FormatArgs) + bw! setlocal formatexpr= + setglobal formatexpr=s:Format() + call assert_equal(expand('<SID>') .. 'Format()', &g:formatexpr) + call assert_equal('', &formatexpr) + new + call assert_equal(expand('<SID>') .. 'Format()', &formatexpr) + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 10GVjgq + call assert_equal([10, 2], g:FormatArgs) + bw! + setglobal formatexpr=<SID>Format() + call assert_equal(expand('<SID>') .. 'Format()', &g:formatexpr) + call assert_equal('', &formatexpr) + new + call assert_equal(expand('<SID>') .. 'Format()', &formatexpr) + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 12GVjgq + call assert_equal([12, 2], g:FormatArgs) + bw! + let &g:formatexpr = 's:Format()' + call assert_equal(expand('<SID>') .. 'Format()', &g:formatexpr) + call assert_equal('', &formatexpr) + new + call assert_equal(expand('<SID>') .. 'Format()', &formatexpr) + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 14GVjgq + call assert_equal([14, 2], g:FormatArgs) + bw! + let &g:formatexpr = '<SID>Format()' + call assert_equal(expand('<SID>') .. 'Format()', &g:formatexpr) + call assert_equal('', &formatexpr) + new + call assert_equal(expand('<SID>') .. 'Format()', &formatexpr) + call setline(1, range(1, 40)) + let g:FormatArgs = [] + normal! 16GVjgq + call assert_equal([16, 2], g:FormatArgs) + bw! + set formatexpr= delfunc s:Format bw! endfunc diff --git a/src/nvim/testdir/test_packadd.vim b/src/nvim/testdir/test_packadd.vim index fcb8b8033b..3121b3b4d1 100644 --- a/src/nvim/testdir/test_packadd.vim +++ b/src/nvim/testdir/test_packadd.vim @@ -26,7 +26,7 @@ func Test_packadd() let rtp_entries = split(rtp, ',') for entry in rtp_entries - if entry =~? '\<after\>' + if entry =~? '\<after\>' let first_after_entry = entry break endif @@ -186,15 +186,17 @@ func Test_packadd_symlink_dir2() exec "silent !rmdir" top2_dir endfunc -" Check command-line completion for 'packadd' +" Check command-line completion for :packadd func Test_packadd_completion() let optdir1 = &packpath . '/pack/mine/opt' let optdir2 = &packpath . '/pack/candidate/opt' call mkdir(optdir1 . '/pluginA', 'p') call mkdir(optdir1 . '/pluginC', 'p') + call writefile([], optdir1 . '/unrelated') call mkdir(optdir2 . '/pluginB', 'p') call mkdir(optdir2 . '/pluginC', 'p') + call writefile([], optdir2 . '/unrelated') let li = [] call feedkeys(":packadd \<Tab>')\<C-B>call add(li, '\<CR>", 't') @@ -260,9 +262,9 @@ func Test_helptags() helptags ALL - let tags1 = readfile(docdir1 . '/tags') + let tags1 = readfile(docdir1 . '/tags') call assert_match('look-here', tags1[0]) - let tags2 = readfile(docdir2 . '/tags') + let tags2 = readfile(docdir2 . '/tags') call assert_match('look-away', tags2[0]) call assert_fails('helptags abcxyz', 'E150:') @@ -358,4 +360,78 @@ func Test_runtime() call assert_equal('runstartopt', g:sequence) endfunc +func Test_runtime_completion() + let rundir = &packpath . '/runtime/Aextra' + let startdir = &packpath . '/pack/mine/start/foo/Aextra' + let optdir = &packpath . '/pack/mine/opt/bar/Aextra' + call mkdir(rundir . '/Arunbaz', 'p') + call mkdir(startdir . '/Astartbaz', 'p') + call mkdir(optdir . '/Aoptbaz', 'p') + call writefile([], rundir . '/../Arunfoo.vim') + call writefile([], rundir . '/Arunbar.vim') + call writefile([], rundir . '/Aunrelated') + call writefile([], rundir . '/../Aunrelated') + call writefile([], startdir . '/../Astartfoo.vim') + call writefile([], startdir . '/Astartbar.vim') + call writefile([], startdir . '/Aunrelated') + call writefile([], startdir . '/../Aunrelated') + call writefile([], optdir . '/../Aoptfoo.vim') + call writefile([], optdir . '/Aoptbar.vim') + call writefile([], optdir . '/Aunrelated') + call writefile([], optdir . '/../Aunrelated') + exe 'set rtp=' . &packpath . '/runtime' + + func Check_runtime_completion(arg, arg1, res) + call feedkeys(':runtime ' .. a:arg .. "\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"runtime ' .. a:arg1 .. join(a:res), @:) + call assert_equal(a:res, getcompletion(a:arg, 'runtime')) + endfunc + + call Check_runtime_completion('', '', + \ ['Aextra/', 'Arunfoo.vim', 'START', 'OPT', 'PACK', 'ALL']) + call Check_runtime_completion('S', '', + \ ['START']) + call Check_runtime_completion('O', '', + \ ['OPT']) + call Check_runtime_completion('P', '', + \ ['PACK']) + call Check_runtime_completion('A', '', + \ ['Aextra/', 'Arunfoo.vim', 'ALL']) + call Check_runtime_completion('Aextra/', '', + \ ['Aextra/Arunbar.vim', 'Aextra/Arunbaz/']) + + call Check_runtime_completion('START ', 'START ', + \ ['Aextra/', 'Astartfoo.vim']) + call Check_runtime_completion('START A', 'START ', + \ ['Aextra/', 'Astartfoo.vim']) + call Check_runtime_completion('START Aextra/', 'START ', + \ ['Aextra/Astartbar.vim', 'Aextra/Astartbaz/']) + + call Check_runtime_completion('OPT ', 'OPT ', + \ ['Aextra/', 'Aoptfoo.vim']) + call Check_runtime_completion('OPT A', 'OPT ', + \ ['Aextra/', 'Aoptfoo.vim']) + call Check_runtime_completion('OPT Aextra/', 'OPT ', + \ ['Aextra/Aoptbar.vim', 'Aextra/Aoptbaz/']) + + call Check_runtime_completion('PACK ', 'PACK ', + \ ['Aextra/', 'Aoptfoo.vim', 'Astartfoo.vim']) + call Check_runtime_completion('PACK A', 'PACK ', + \ ['Aextra/', 'Aoptfoo.vim', 'Astartfoo.vim']) + call Check_runtime_completion('PACK Aextra/', 'PACK ', + \ ['Aextra/Aoptbar.vim', 'Aextra/Aoptbaz/', + \ 'Aextra/Astartbar.vim', 'Aextra/Astartbaz/']) + + call Check_runtime_completion('ALL ', 'ALL ', + \ ['Aextra/', 'Aoptfoo.vim', 'Arunfoo.vim', 'Astartfoo.vim']) + call Check_runtime_completion('ALL A', 'ALL ', + \ ['Aextra/', 'Aoptfoo.vim', 'Arunfoo.vim', 'Astartfoo.vim']) + call Check_runtime_completion('ALL Aextra/', 'ALL ', + \ ['Aextra/Aoptbar.vim', 'Aextra/Aoptbaz/', + \ 'Aextra/Arunbar.vim', 'Aextra/Arunbaz/', + \ 'Aextra/Astartbar.vim', 'Aextra/Astartbaz/']) + + delfunc Check_runtime_completion +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_search_stat.vim b/src/nvim/testdir/test_search_stat.vim index 77bd50ada2..1b2d854829 100644 --- a/src/nvim/testdir/test_search_stat.vim +++ b/src/nvim/testdir/test_search_stat.vim @@ -270,6 +270,29 @@ func Test_searchcount_fails() call assert_fails('echo searchcount({"pos" : [1, 2, []]})', 'E745:') endfunc +func Test_search_stat_narrow_screen() + " This used to crash Vim + let save_columns = &columns + try + let after =<< trim [CODE] + set laststatus=2 + set columns=16 + set shortmess-=S showcmd + call setline(1, 'abc') + call feedkeys("/abc\<CR>:quit!\<CR>") + autocmd VimLeavePre * call writefile(["done"], "Xdone") + [CODE] + + if !RunVim([], after, '--clean') + return + endif + call assert_equal("done", readfile("Xdone")[0]) + call delete('Xdone') + finally + let &columns = save_columns + endtry +endfunc + func Test_searchcount_in_statusline() CheckScreendump diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim index 0387ef2bd8..75d28c3ec4 100644 --- a/src/nvim/testdir/test_taglist.vim +++ b/src/nvim/testdir/test_taglist.vim @@ -105,8 +105,8 @@ func Test_tagfiles() help let tf = tagfiles() " Nvim: expectation(s) based on tags in build dir (added to &rtp). - " Filter out the (non-existing) '../../../runtime/doc/tags'. - call filter(tf, 'filereadable(v:val)') + " Filter out the '../../../runtime/doc/tags'. + call filter(tf, 'v:val != "../../../runtime/doc/tags"') call assert_equal(1, len(tf)) call assert_equal(fnamemodify(expand('$BUILD_DIR/runtime/doc/tags'), ':p:gs?\\?/?'), \ fnamemodify(tf[0], ':p:gs?\\?/?')) diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim index 2bf8c3fc77..20a5f87517 100644 --- a/src/nvim/testdir/test_virtualedit.vim +++ b/src/nvim/testdir/test_virtualedit.vim @@ -537,6 +537,19 @@ func Test_global_local_virtualedit() set virtualedit& endfunc +func Test_virtualedit_setlocal() + enew + setglobal virtualedit=all + setlocal virtualedit=all + normal! l + redraw + setlocal virtualedit=none + call assert_equal(1, wincol()) + + setlocal virtualedit& + set virtualedit& +endfunc + func Test_virtualedit_mouse() let save_mouse = &mouse set mouse=a diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 14d62089cf..1e9629c2c4 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1319,6 +1319,17 @@ func Test_visual_block_with_substitute() endfunc func Test_visual_reselect_with_count() + enew + call setline(1, ['aaaaaa', '✗ bbbb', '✗ bbbb']) + exe "normal! 2Gw\<C-V>jed" + exe "normal! gg0lP" + call assert_equal(['abbbbaaaaa', '✗bbbb ', '✗ '], getline(1, '$')) + + exe "normal! 1vr." + call assert_equal(['a....aaaaa', '✗.... ', '✗ '], getline(1, '$')) + + bwipe! + " this was causing an illegal memory access let lines =<< trim END diff --git a/src/nvim/testing.c b/src/nvim/testing.c index edf92c78ac..b5921b3445 100644 --- a/src/nvim/testing.c +++ b/src/nvim/testing.c @@ -77,31 +77,32 @@ static void ga_concat_esc(garray_T *gap, const char *p, int clen) memmove(buf, p, (size_t)clen); buf[clen] = NUL; ga_concat(gap, buf); - } else { - switch (*p) { - case BS: - ga_concat(gap, "\\b"); break; - case ESC: - ga_concat(gap, "\\e"); break; - case FF: - ga_concat(gap, "\\f"); break; - case NL: - ga_concat(gap, "\\n"); break; - case TAB: - ga_concat(gap, "\\t"); break; - case CAR: - ga_concat(gap, "\\r"); break; - case '\\': - ga_concat(gap, "\\\\"); break; - default: - if ((uint8_t)(*p) < ' ' || *p == 0x7f) { - vim_snprintf(buf, NUMBUFLEN, "\\x%02x", *p); - ga_concat(gap, buf); - } else { - ga_append(gap, (uint8_t)(*p)); - } - break; + return; + } + + switch (*p) { + case BS: + ga_concat(gap, "\\b"); break; + case ESC: + ga_concat(gap, "\\e"); break; + case FF: + ga_concat(gap, "\\f"); break; + case NL: + ga_concat(gap, "\\n"); break; + case TAB: + ga_concat(gap, "\\t"); break; + case CAR: + ga_concat(gap, "\\r"); break; + case '\\': + ga_concat(gap, "\\\\"); break; + default: + if ((uint8_t)(*p) < ' ' || *p == 0x7f) { + vim_snprintf(buf, NUMBUFLEN, "\\x%02x", *p); + ga_concat(gap, buf); + } else { + ga_append(gap, (uint8_t)(*p)); } + break; } } diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c index fbea1ccfb7..e30580a748 100644 --- a/src/nvim/textformat.c +++ b/src/nvim/textformat.c @@ -736,22 +736,24 @@ void check_auto_format(bool end_insert) int c = ' '; int cc; - if (did_add_space) { - cc = gchar_cursor(); - if (!WHITECHAR(cc)) { - // Somehow the space was removed already. + if (!did_add_space) { + return; + } + + cc = gchar_cursor(); + if (!WHITECHAR(cc)) { + // Somehow the space was removed already. + did_add_space = false; + } else { + if (!end_insert) { + inc_cursor(); + c = gchar_cursor(); + dec_cursor(); + } + if (c != NUL) { + // The space is no longer at the end of the line, delete it. + del_char(false); did_add_space = false; - } else { - if (!end_insert) { - inc_cursor(); - c = gchar_cursor(); - dec_cursor(); - } - if (c != NUL) { - // The space is no longer at the end of the line, delete it. - del_char(false); - did_add_space = false; - } } } } diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 2cb39ab26b..733aa25f03 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -117,14 +117,6 @@ static const struct kitty_key_map_entry { static Map(KittyKey, cstr_t) kitty_key_map = MAP_INIT; -#ifndef UNIT_TESTING -typedef enum { - kIncomplete = -1, - kNotApplicable = 0, - kComplete = 1, -} HandleState; -#endif - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/input.c.generated.h" #endif @@ -584,7 +576,7 @@ static void set_bg(char *bgvalue) // ignored in the calculations. // // [1] https://en.wikipedia.org/wiki/Luma_%28video%29 -static HandleState handle_background_color(TermInput *input) +HandleState handle_background_color(TermInput *input) { if (input->waiting_for_bg_response <= 0) { return kNotApplicable; @@ -669,12 +661,6 @@ static HandleState handle_background_color(TermInput *input) } return kComplete; } -#ifdef UNIT_TESTING -HandleState ut_handle_background_color(TermInput *input) -{ - return handle_background_color(input); -} -#endif static void handle_raw_buffer(TermInput *input, bool force) { diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h index 5df108b107..d33cea6383 100644 --- a/src/nvim/tui/input.h +++ b/src/nvim/tui/input.h @@ -40,18 +40,14 @@ typedef struct term_input { TUIData *tui_data; } TermInput; -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "tui/input.h.generated.h" -#endif - -#ifdef UNIT_TESTING typedef enum { kIncomplete = -1, kNotApplicable = 0, kComplete = 1, } HandleState; -HandleState ut_handle_background_color(TermInput *input); +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "tui/input.h.generated.h" #endif #endif // NVIM_TUI_INPUT_H diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index a50e44f7a3..f760e99262 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -458,6 +458,9 @@ static void tui_terminal_stop(TUIData *tui) void tui_stop(TUIData *tui) { + if (tui->stopped) { + return; + } tui_terminal_stop(tui); stream_set_blocking(tui->input.in_fd, true); // normalize stream (#2598) tinput_destroy(&tui->input); @@ -1344,6 +1347,7 @@ static void show_verbose_terminfo(TUIData *tui) static void suspend_event(void **argv) { TUIData *tui = argv[0]; + ui_client_detach(); bool enable_mouse = tui->mouse_enabled; tui_terminal_stop(tui); stream_set_blocking(tui->input.in_fd, true); // normalize stream (#2598) @@ -1356,6 +1360,7 @@ static void suspend_event(void **argv) tui_mouse_on(tui); } stream_set_blocking(tui->input.in_fd, false); // libuv expects this + ui_client_attach(tui->width, tui->height, tui->term); } #endif diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 378c0e4831..b5c8dff412 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -23,6 +23,7 @@ #include "nvim/ui_client.h" static TUIData *tui = NULL; +static bool ui_client_is_remote = false; // uncrustify:off #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -66,13 +67,8 @@ uint64_t ui_client_start_server(int argc, char **argv) return channel->id; } -void ui_client_run(bool remote_ui) - FUNC_ATTR_NORETURN +void ui_client_attach(int width, int height, char *term) { - int width, height; - char *term; - tui = tui_start(&width, &height, &term); - MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, INTEGER_OBJ(width)); ADD_C(args, INTEGER_OBJ(height)); @@ -82,14 +78,14 @@ void ui_client_run(bool remote_ui) PUT_C(opts, "ext_linegrid", BOOLEAN_OBJ(true)); PUT_C(opts, "ext_termcolors", BOOLEAN_OBJ(true)); if (term) { - PUT(opts, "term_name", STRING_OBJ(cstr_to_string(term))); + PUT_C(opts, "term_name", STRING_OBJ(cstr_as_string(term))); } if (ui_client_bg_response != kNone) { bool is_dark = (ui_client_bg_response == kTrue); PUT_C(opts, "term_background", STRING_OBJ(cstr_as_string(is_dark ? "dark" : "light"))); } PUT_C(opts, "term_colors", INTEGER_OBJ(t_colors)); - if (!remote_ui) { + if (!ui_client_is_remote) { PUT_C(opts, "stdin_tty", BOOLEAN_OBJ(stdin_isatty)); PUT_C(opts, "stdout_tty", BOOLEAN_OBJ(stdout_isatty)); if (ui_client_forward_stdin) { @@ -100,6 +96,23 @@ void ui_client_run(bool remote_ui) rpc_send_event(ui_client_channel_id, "nvim_ui_attach", args); ui_client_attached = true; +} + +void ui_client_detach(void) +{ + rpc_send_event(ui_client_channel_id, "nvim_ui_detach", (Array)ARRAY_DICT_INIT); + ui_client_attached = false; +} + +void ui_client_run(bool remote_ui) + FUNC_ATTR_NORETURN +{ + ui_client_is_remote = remote_ui; + int width, height; + char *term; + tui = tui_start(&width, &height, &term); + + ui_client_attach(width, height, term); // os_exit() will be invoked when the client channel detaches while (true) { diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 2b0bb1d243..0f12c00f15 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2985,10 +2985,12 @@ void u_saveline(linenr_T lnum) /// (this is used externally for crossing a line while in insert mode) void u_clearline(void) { - if (curbuf->b_u_line_ptr != NULL) { - XFREE_CLEAR(curbuf->b_u_line_ptr); - curbuf->b_u_line_lnum = 0; + if (curbuf->b_u_line_ptr == NULL) { + return; } + + XFREE_CLEAR(curbuf->b_u_line_ptr); + curbuf->b_u_line_lnum = 0; } /// Implementation of the "U" command. diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 31cb1e8936..ef13f67e49 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -89,6 +89,7 @@ static const char *command_complete[] = { [EXPAND_SYNTIME] = "syntime", [EXPAND_SETTINGS] = "option", [EXPAND_PACKADD] = "packadd", + [EXPAND_RUNTIME] = "runtime", [EXPAND_SHELLCMD] = "shellcmd", [EXPAND_SIGN] = "sign", [EXPAND_TAGS] = "tag", diff --git a/src/nvim/version.c b/src/nvim/version.c index 417e5116a5..3324ac2a94 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -795,7 +795,7 @@ static const int included_patches[] = { 1702, 1701, // 1700, - // 1699, + 1699, 1698, 1697, 1696, @@ -1361,7 +1361,7 @@ static const int included_patches[] = { // 1136, 1135, 1134, - // 1133, + 1133, 1132, 1131, 1130, @@ -1674,7 +1674,7 @@ static const int included_patches[] = { 823, 822, 821, - // 820, + 820, 819, 818, 817, @@ -2785,13 +2785,6 @@ void maybe_intro_message(void) /// @param colon true for ":intro" void intro_message(int colon) { - int i; - long row; - long blanklines; - int sponsor; - char *p; - char *mesg; - int mesg_size; static char *(lines[]) = { N_(NVIM_VERSION_LONG), "", @@ -2813,7 +2806,7 @@ void intro_message(int colon) size_t lines_size = ARRAY_SIZE(lines); assert(lines_size <= LONG_MAX); - blanklines = Rows - ((long)lines_size - 1L); + long blanklines = Rows - ((long)lines_size - 1L); // Don't overwrite a statusline. Depends on 'cmdheight'. if (p_ls > 1) { @@ -2826,17 +2819,17 @@ void intro_message(int colon) // Show the sponsor and register message one out of four times, the Uganda // message two out of four times. - sponsor = (int)time(NULL); + int sponsor = (int)time(NULL); sponsor = ((sponsor & 2) == 0) - ((sponsor & 4) == 0); // start displaying the message lines after half of the blank lines - row = blanklines / 2; + long row = blanklines / 2; if (((row >= 2) && (Columns >= 50)) || colon) { - for (i = 0; i < (int)ARRAY_SIZE(lines); i++) { - p = lines[i]; - mesg = NULL; - mesg_size = 0; + for (int i = 0; i < (int)ARRAY_SIZE(lines); i++) { + char *p = lines[i]; + char *mesg = NULL; + int mesg_size = 0; if (strstr(p, "news") != NULL) { p = _(p); @@ -2846,18 +2839,15 @@ void intro_message(int colon) mesg = xmallocz((size_t)mesg_size); snprintf(mesg, (size_t)mesg_size + 1, p, STR(NVIM_VERSION_MAJOR), STR(NVIM_VERSION_MINOR)); - } - - if (sponsor != 0) { + } else if (sponsor != 0) { if (strstr(p, "children") != NULL) { - mesg = sponsor < 0 - ? _("Sponsor Vim development!") - : _("Become a registered Vim user!"); - } - if (strstr(p, "iccf") != NULL) { - mesg = sponsor < 0 - ? _("type :help sponsor<Enter> for information ") - : _("type :help register<Enter> for information "); + p = sponsor < 0 + ? N_("Sponsor Vim development!") + : N_("Become a registered Vim user!"); + } else if (strstr(p, "iccf") != NULL) { + p = sponsor < 0 + ? N_("type :help sponsor<Enter> for information ") + : N_("type :help register<Enter> for information "); } } diff --git a/src/nvim/vim.h b/src/nvim/vim.h index c4baa911f1..3c765f8eb2 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -156,6 +156,7 @@ enum { EXPAND_DIFF_BUFFERS, EXPAND_BREAKPOINT, EXPAND_SCRIPTNAMES, + EXPAND_RUNTIME, EXPAND_CHECKHEALTH, EXPAND_LUA, }; diff --git a/src/nvim/window.c b/src/nvim/window.c index 16405f87b6..4448b72ac0 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2599,6 +2599,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev if (!ONE_WINDOW) { return false; } + buf_T *old_curbuf = curbuf; Terminal *term = win->w_buffer ? win->w_buffer->terminal : NULL; @@ -4144,12 +4145,13 @@ int may_open_tabpage(void) { int n = (cmdmod.cmod_tab == 0) ? postponed_split_tab : cmdmod.cmod_tab; - if (n != 0) { - cmdmod.cmod_tab = 0; // reset it to avoid doing it twice - postponed_split_tab = 0; - return win_new_tabpage(n, NULL); + if (n == 0) { + return FAIL; } - return FAIL; + + cmdmod.cmod_tab = 0; // reset it to avoid doing it twice + postponed_split_tab = 0; + return win_new_tabpage(n, NULL); } // Create up to "maxcount" tabpages with empty windows. @@ -4494,11 +4496,12 @@ void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_le /// @return true if the tab page is valid, false otherwise. bool goto_tabpage_lastused(void) { - if (valid_tabpage(lastused_tabpage)) { - goto_tabpage_tp(lastused_tabpage, true, true); - return true; + if (!valid_tabpage(lastused_tabpage)) { + return false; } - return false; + + goto_tabpage_tp(lastused_tabpage, true, true); + return true; } // Enter window "wp" in tab page "tp". @@ -7261,11 +7264,12 @@ static void clear_snapshot(tabpage_T *tp, int idx) static void clear_snapshot_rec(frame_T *fr) { - if (fr != NULL) { - clear_snapshot_rec(fr->fr_next); - clear_snapshot_rec(fr->fr_child); - xfree(fr); + if (fr == NULL) { + return; } + clear_snapshot_rec(fr->fr_next); + clear_snapshot_rec(fr->fr_child); + xfree(fr); } /// Traverse a snapshot to find the previous curwin. diff --git a/test/busted_runner.lua b/test/busted_runner.lua new file mode 100644 index 0000000000..b195ce3cc5 --- /dev/null +++ b/test/busted_runner.lua @@ -0,0 +1 @@ +require 'busted.runner'({ standalone = false }) diff --git a/test/cmakeconfig/paths.lua.in b/test/cmakeconfig/paths.lua.in index 6be238d838..a35dbe8901 100644 --- a/test/cmakeconfig/paths.lua.in +++ b/test/cmakeconfig/paths.lua.in @@ -6,7 +6,6 @@ for p in ("${TEST_INCLUDE_DIRS}" .. ";"):gmatch("[^;]+") do end module.test_build_dir = "${CMAKE_BINARY_DIR}" -module.test_libnvim_path = "${TEST_LIBNVIM_PATH}" module.test_source_path = "${CMAKE_SOURCE_DIR}" module.test_lua_prg = "${LUA_PRG}" module.test_luajit_prg = "" diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index 00f5b25b8a..9902826c72 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -1454,6 +1454,14 @@ describe('API/extmarks', function() }} }, get_extmarks(ns, 0, -1, {details=true})) end) + it('in prompt buffer', function() + feed('dd') + local id = set_extmark(ns, marks[1], 0, 0, {}) + curbufmeths.set_option('buftype', 'prompt') + feed('i<esc>') + eq({{id, 0, 2}}, get_extmarks(ns, 0, -1)) + end) + it('can get details', function() set_extmark(ns, marks[1], 0, 0, { end_col = 0, diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 5941d4c68b..7f044977cb 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -214,7 +214,7 @@ describe("API: set highlight", function() bold = true, italic = true, reverse = true, - underline = true, + underdashed = true, strikethrough = true, altfont = true, cterm = { @@ -231,7 +231,7 @@ describe("API: set highlight", function() bold = true, italic = true, reverse = true, - underline = true, + underdashed = true, strikethrough = true, altfont = true, } @@ -297,7 +297,7 @@ describe("API: set highlight", function() exec_capture('highlight Test_hl')) meths.set_hl(0, 'Test_hl2', highlight3_config) - eq('Test_hl2 xxx cterm=italic,reverse,strikethrough,altfont,nocombine ctermfg=8 ctermbg=15 gui=bold,underline,italic,reverse,strikethrough,altfont guifg=#ff0000 guibg=#0032aa', + eq('Test_hl2 xxx cterm=italic,reverse,strikethrough,altfont,nocombine ctermfg=8 ctermbg=15 gui=bold,underdashed,italic,reverse,strikethrough,altfont guifg=#ff0000 guibg=#0032aa', exec_capture('highlight Test_hl2')) -- Colors are stored with the name they are defined, but diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 1be5de6488..e9b47a0251 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -102,6 +102,13 @@ describe('startup', function() end) it('os.exit() sets Nvim exitcode', function() + -- tricky: LeakSanitizer triggers on os.exit() and disrupts the return value, disable it + exec_lua [[ + local asan_options = os.getenv 'ASAN_OPTIONS' + if asan_options ~= nil and asan_options ~= '' then + vim.loop.os_setenv('ASAN_OPTIONS', asan_options..':detect_leaks=0') + end + ]] -- nvim -l foo.lua -arg1 -- a b c assert_l_out([[ bufs: diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index aa47198f7a..db0c8c0c3f 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -927,10 +927,13 @@ function tests.basic_formatting() } end --- Tests will be indexed by TEST_NAME +-- Tests will be indexed by test_name +local test_name = arg[1] +local timeout = arg[2] +assert(type(test_name) == 'string', 'test_name must be specified as first arg.') local kill_timer = vim.loop.new_timer() -kill_timer:start(_G.TIMEOUT or 1e3, 0, function() +kill_timer:start(timeout or 1e3, 0, function() kill_timer:stop() kill_timer:close() log('ERROR', 'LSP', 'TIMEOUT') @@ -938,14 +941,11 @@ kill_timer:start(_G.TIMEOUT or 1e3, 0, function() os.exit(100) end) -local test_name = _G.TEST_NAME -- lualint workaround -assert(type(test_name) == 'string', 'TEST_NAME must be specified.') local status, err = pcall(assert(tests[test_name], "Test not found")) kill_timer:stop() kill_timer:close() if not status then log('ERROR', 'LSP', tostring(err)) io.stderr:write(err) - os.exit(101) + vim.cmd [[101cquit]] end -os.exit(0) diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua index b659f2eacb..884ef3ef8e 100644 --- a/test/functional/lua/runtime_spec.lua +++ b/test/functional/lua/runtime_spec.lua @@ -18,6 +18,7 @@ describe('runtime:', function() io.open(init, 'w'):close() -- touch init file clear{args = {'-u', init}} exec('set rtp+=' .. plug_dir) + exec('set completeslash=slash') end) teardown(function() @@ -42,6 +43,7 @@ describe('runtime:', function() write_file(colorscheme_file, [[vim.g.lua_colorscheme = 1]]) eq({'new_colorscheme'}, funcs.getcompletion('new_c', 'color')) + eq({'colors/new_colorscheme.lua'}, funcs.getcompletion('colors/new_c', 'runtime')) exec('colorscheme new_colorscheme') @@ -71,6 +73,7 @@ describe('runtime:', function() write_file(compiler_file, [[vim.b.lua_compiler = 1]]) eq({'new_compiler'}, funcs.getcompletion('new_c', 'compiler')) + eq({'compiler/new_compiler.lua'}, funcs.getcompletion('compiler/new_c', 'runtime')) exec('compiler new_compiler') @@ -100,6 +103,7 @@ describe('runtime:', function() write_file(ftplugin_file , [[vim.b.lua_ftplugin = 1]]) eq({'new-ft'}, funcs.getcompletion('new-f', 'filetype')) + eq({'ftplugin/new-ft.lua'}, funcs.getcompletion('ftplugin/new-f', 'runtime')) exec [[set filetype=new-ft]] eq(1, eval('b:lua_ftplugin')) @@ -116,6 +120,7 @@ describe('runtime:', function() write_file(indent_file , [[vim.b.lua_indent = 1]]) eq({'new-ft'}, funcs.getcompletion('new-f', 'filetype')) + eq({'indent/new-ft.lua'}, funcs.getcompletion('indent/new-f', 'runtime')) exec [[set filetype=new-ft]] eq(1, eval('b:lua_indent')) @@ -152,6 +157,7 @@ describe('runtime:', function() it('lua syntaxes are included in cmdline completion', function() eq({'my-lang'}, funcs.getcompletion('my-l', 'filetype')) eq({'my-lang'}, funcs.getcompletion('my-l', 'syntax')) + eq({'syntax/my-lang.lua'}, funcs.getcompletion('syntax/my-l', 'runtime')) end) end) diff --git a/test/functional/plugin/lsp/helpers.lua b/test/functional/plugin/lsp/helpers.lua index 028ccb9e2c..caab174b4d 100644 --- a/test/functional/plugin/lsp/helpers.lua +++ b/test/functional/plugin/lsp/helpers.lua @@ -80,17 +80,14 @@ M.fake_lsp_logfile = 'Xtest-fake-lsp.log' local function fake_lsp_server_setup(test_name, timeout_ms, options, settings) exec_lua([=[ lsp = require('vim.lsp') - local test_name, fixture_filename, logfile, timeout, options, settings = ... + local test_name, fake_lsp_code, fake_lsp_logfile, timeout, options, settings = ... TEST_RPC_CLIENT_ID = lsp.start_client { cmd_env = { - NVIM_LOG_FILE = logfile; + NVIM_LOG_FILE = fake_lsp_logfile; NVIM_LUA_NOTRACK = "1"; }; cmd = { - vim.v.progpath, '-Es', '-u', 'NONE', '--headless', - "-c", string.format("lua TEST_NAME = %q", test_name), - "-c", string.format("lua TIMEOUT = %d", timeout), - "-c", "luafile "..fixture_filename, + vim.v.progpath, '-l', fake_lsp_code, test_name, tostring(timeout), }; handlers = setmetatable({}, { __index = function(t, method) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 5229022564..fd162961ff 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -46,16 +46,14 @@ describe('LSP', function() local test_name = "basic_init" exec_lua([=[ lsp = require('vim.lsp') - local test_name, fixture_filename, logfile = ... + local test_name, fake_lsp_code, fake_lsp_logfile = ... function test__start_client() return lsp.start_client { cmd_env = { - NVIM_LOG_FILE = logfile; + NVIM_LOG_FILE = fake_lsp_logfile; }; cmd = { - vim.v.progpath, '-Es', '-u', 'NONE', '--headless', - "-c", string.format("lua TEST_NAME = %q", test_name), - "-c", "luafile "..fixture_filename; + vim.v.progpath, '-l', fake_lsp_code, test_name; }; workspace_folders = {{ uri = 'file://' .. vim.loop.cwd(), diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua index c6c7d2b03d..58da059be6 100644 --- a/test/functional/plugin/man_spec.lua +++ b/test/functional/plugin/man_spec.lua @@ -59,7 +59,7 @@ describe(':Man', function() screen:expect([[ ^this {b:is} {b:a} test | - with {u:overstruck} text | + with {i:overstruck} text | {eob:~ }| {eob:~ }| | @@ -98,7 +98,7 @@ describe(':Man', function() screen:expect([[ ^this {b:is} {b:あ} test | - with {u:överstrũck} te{i:xt¶} | + with {i:överstrũck} te{i:xt¶} | {eob:~ }| {eob:~ }| | @@ -115,7 +115,7 @@ describe(':Man', function() screen:expect([[ {b:^_begins} | {b:mid_dle} | - {u:mid_dle} | + {i:mid_dle} | {eob:~ }| | ]]) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index b28728057f..1d9e7b8e11 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -14,6 +14,7 @@ local clear = helpers.clear local command = helpers.command local dedent = helpers.dedent local exec = helpers.exec +local exec_lua = helpers.exec_lua local testprg = helpers.testprg local retry = helpers.retry local nvim_prog = helpers.nvim_prog @@ -1506,6 +1507,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]} end) + + it('no assert failure on deadly signal #21896', function() + exec_lua([[vim.loop.kill(vim.fn.jobpid(vim.bo.channel), 'sigterm')]]) + screen:expect({any = '%[Process exited 1%]'}) + end) end) describe('TUI', function() diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index f95b05a1cc..df45c9b384 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -31,6 +31,11 @@ describe('treesitter language API', function() pcall_err(exec_lua, 'vim.treesitter.require_language("c", nil, false, "borklang")')) end) + it('shows error for invalid language name', function() + eq(".../language.lua:0: '/foo/' is not a valid language name", + pcall_err(exec_lua, 'vim.treesitter.require_language("/foo/", nil, false)')) + end) + it('inspects language', function() local keys, fields, symbols = unpack(exec_lua([[ local lang = vim.treesitter.inspect_language('c') diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua index ae3b95fb0f..3233e6cd19 100644 --- a/test/functional/ui/statuscolumn_spec.lua +++ b/test/functional/ui/statuscolumn_spec.lua @@ -439,7 +439,7 @@ describe('statuscolumn', function() vim.api.nvim_buf_set_extmark(0, ns, 7, 0, { virt_lines_leftcol = true, virt_lines = {{{"virt", ""}}} }) ]]) - feed('lh') -- force update wcol/row + feed('lh') -- force update cursor row screen:expect([[ 4 aaaaa | 5 aaaaa | @@ -458,5 +458,24 @@ describe('statuscolumn', function() ]]) command('set stc=') -- also for the default sign column screen:expect_unchanged() + -- 'statuscolumn' is not too wide with custom (bogus) fold column + command([[set stc=%{foldlevel(v:lnum)>0?repeat('-',foldlevel(v:lnum)):''}%=%l\ ]]) + feed('Gd10Ggg<C-l>') + screen:expect([[ + 1 ^aaaaa | + 2 aaaaa | + 3 aaaaa | + 4 aaaaa | + 5 aaaaa | + 6 aaaaa | + 7 aaaaa | + virt | + ---------8 aaaaa | + virt | + ---------9 aaaaa | + ~ | + ~ | + | + ]]) end) end) diff --git a/test/functional/vimscript/exepath_spec.lua b/test/functional/vimscript/exepath_spec.lua index 056f67e0ad..da3d61cbe0 100644 --- a/test/functional/vimscript/exepath_spec.lua +++ b/test/functional/vimscript/exepath_spec.lua @@ -5,21 +5,21 @@ local command = helpers.command local exc_exec = helpers.exc_exec local matches = helpers.matches local is_os = helpers.is_os +local set_shell_powershell = helpers.set_shell_powershell +local eval = helpers.eval + +local find_dummies = function(ext_pat) + local tmp_path = eval('$PATH') + command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') + matches('null' .. ext_pat, call('exepath', 'null')) + matches('true' .. ext_pat, call('exepath', 'true')) + matches('false' .. ext_pat, call('exepath', 'false')) + command("let $PATH = '"..tmp_path.."'") +end describe('exepath()', function() before_each(clear) - it('returns 1 for commands in $PATH', function() - local exe = is_os('win') and 'ping' or 'ls' - local ext_pat = is_os('win') and '%.EXE$' or '$' - matches(exe .. ext_pat, call('exepath', exe)) - command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') - ext_pat = is_os('win') and '%.CMD$' or '$' - matches('null' .. ext_pat, call('exepath', 'null')) - matches('true' .. ext_pat, call('exepath', 'true')) - matches('false' .. ext_pat, call('exepath', 'false')) - end) - it('fails for invalid values', function() for _, input in ipairs({'v:null', 'v:true', 'v:false', '{}', '[]'}) do eq('Vim(call):E1174: String required for argument 1', exc_exec('call exepath('..input..')')) @@ -32,11 +32,32 @@ describe('exepath()', function() end) if is_os('win') then + it('returns 1 for commands in $PATH (Windows)', function() + local exe = 'ping' + matches(exe .. '%.EXE$', call('exepath', exe)) + end) + it('append extension if omitted', function() local filename = 'cmd' local pathext = '.exe' clear({env={PATHEXT=pathext}}) eq(call('exepath', filename..pathext), call('exepath', filename)) end) + + it('returns file WITH extension if files both with and without extension exist in $PATH', function() + local ext_pat = '%.CMD$' + find_dummies(ext_pat) + set_shell_powershell() + find_dummies(ext_pat) + end) + else + it('returns 1 for commands in $PATH (not Windows)', function() + local exe = 'ls' + matches(exe .. '$', call('exepath', exe)) + end) + + it('returns file WITHOUT extension if files both with and without extension exist in $PATH', function() + find_dummies('$') + end) end end) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 128625374e..825377813d 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1677,7 +1677,7 @@ describe('typval.c', function() eq(nil, lib.tv_dict_find(nil, 'test', -1)) eq(nil, lib.tv_dict_find(nil, nil, 0)) end) - itp('works with NULL key', function() + itp('works with empty key', function() local lua_d = { ['']=0, t=1, @@ -1692,7 +1692,6 @@ describe('typval.c', function() alloc_log:check({}) local dis = dict_items(d) eq({0, '', dis['']}, {tv_dict_find(d, '', 0)}) - eq({0, '', dis['']}, {tv_dict_find(d, nil, 0)}) end) itp('works with len properly', function() local lua_d = { @@ -1911,8 +1910,6 @@ describe('typval.c', function() local d = dict(lua_d) eq(lua_d, dct2tbl(d)) eq({{type='fref', fref='tr'}, true}, - {tv_dict_get_callback(d, nil, 0)}) - eq({{type='fref', fref='tr'}, true}, {tv_dict_get_callback(d, '', -1)}) eq({{type='none'}, true}, {tv_dict_get_callback(d, 'x', -1)}) diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 14d8eda357..686af3b461 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -75,7 +75,8 @@ local function child_cleanup_once(func, ...) end end -local libnvim = nil +-- Unittests are run from debug nvim binary in lua interpreter mode. +local libnvim = ffi.C local lib = setmetatable({}, { __index = only_separate(function(_, idx) @@ -87,8 +88,6 @@ local lib = setmetatable({}, { }) local init = only_separate(function() - -- load neovim shared library - libnvim = ffi.load(Paths.test_libnvim_path) for _, c in ipairs(child_calls_init) do c.func(unpack(c.args)) end diff --git a/test/unit/tui_spec.lua b/test/unit/tui_spec.lua index 25b70a17c2..192e35a485 100644 --- a/test/unit/tui_spec.lua +++ b/test/unit/tui_spec.lua @@ -12,7 +12,7 @@ local multiqueue = cimport("./test/unit/fixtures/multiqueue.h") local ui_client = cimport("./src/nvim/ui_client.h") itp('handle_background_color', function() - local handle_background_color = cinput.ut_handle_background_color + local handle_background_color = cinput.handle_background_color local term_input = ffi.new('TermInput', {}) local events = globals.main_loop.thread_events local kIncomplete = cinput.kIncomplete |