diff options
40 files changed, 883 insertions, 826 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..2c3fbf9ef2 100755 --- a/.github/workflows/env.sh +++ b/.github/workflows/env.sh @@ -44,13 +44,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/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/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..e73775d973 --- /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(main_lib INTERFACE ${LIBVTERM_LIBRARY}) diff --git a/cmake/Findunibilium.cmake b/cmake/Findunibilium.cmake new file mode 100644 index 0000000000..fd8eb0cd7f --- /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(main_lib INTERFACE NVIM_UNIBI_HAS_VAR_FROM) +endif() diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 4d2c85b134..c8f5570bae 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -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 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/lua/man.lua b/runtime/lua/man.lua index a644dd68b8..732a4ab92e 100644 --- a/runtime/lua/man.lua +++ b/runtime/lua/man.lua @@ -420,6 +420,10 @@ local function extract_sect_and_name_path(path) end local function find_man() + if vim.bo.filetype == 'man' then + return true + end + local win = 1 while win <= fn.winnr('$') do local buf = fn.winbufnr(win) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 2361210e59..77ed0490d8 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}) @@ -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> 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/eval/funcs.c b/src/nvim/eval/funcs.c index 5a31288ecd..938fef9a52 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"); @@ -7823,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 @@ -8654,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 22c5b1954d..6c6dc3fa43 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/fileio.c b/src/nvim/fileio.c index 9da9d6199e..6b3e7dddb5 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,6 @@ 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); } @@ -5511,9 +5447,13 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs return xstrdup("^$"); } + if (allow_dirs != NULL) { + *allow_dirs = false; + } + 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..51554fea22 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -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/option.c b/src/nvim/option.c index b8e234981a..387b94533c 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4898,12 +4898,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) { @@ -4936,8 +4936,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..5ebff9ed77 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 == '%'); @@ -957,7 +957,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++; @@ -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 != ' ') { @@ -1497,12 +1497,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 +1511,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; } } @@ -1808,23 +1808,18 @@ static char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx, char did_set_varsoftabstop(buf, varp, &errmsg); } 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' set_completefunc_option(&errmsg); } else if (gvarp == &p_ofu) { // 'omnifunc' 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/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/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_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_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/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/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..11f1d6695e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -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 05f84b5a91..6b40ccdb84 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". @@ -7250,11 +7253,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/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/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) |